【数据库】实现批量快速插入大量数据的六种方案

一、(MySQL)通过函数/存储过程

1、链接

https://www.jb51.net/article/207999.htm

https://blog.csdn.net/FloraCHY/article/details/117792903

2、代码

-- 进入数据库
use test;
-- 显示所有表
show tables;
-- 创建majors表
create table majors(id int, major varchar(255));
-- 定义结束符$
delimiter "$";
-- 创建存储过程,定义存储方法
create procedure batchInsert(in args int)
begin
declare i int default 1;
-- 开启事务(重要!不开的话,100w数据需要论天算)
start transaction;
while i <= args do
insert into majors(id,major) value(i,concat("软件工程-",i));
set i = i+ 1;
end while;
commit;
end
$
 
-- 调用函数,生成数据
-- 先生成10w条试试,同时输入$, 回车执行
call batchInsert(100000);
$

3、性能

10000条数据用了0.9s

100000条,5s执行完

100w条数据用了58s

二、通过jdbc的批量插入语句(add/executeBatch)

1、链接

http://t.zoukankan.com/lizm166-p-7890168.html

2、代码

//获取要设置的Arp基准的List后,插入Arp基准表中    
    public boolean insertArpStandardList(List<ArpTable> list) {
        Connection conn = null;
        PreparedStatement ps = null;
        ResultSet rs = null;
        //MySql的JDBC连接的url中要加rewriteBatchedStatements参数,并保证5.1.13以上版本的驱动,才能实现高性能的批量插入。
        //优化插入性能,用JDBC的addBatch方法,但是注意在连接字符串加上面写的参数。
        //例如: String connectionUrl="jdbc:mysql://192.168.1.100:3306/test?rewriteBatchedStatements=true" ;
        String sql = "insert into arp_standard(guid, devicebrand, devicename, deviceip, ipaddress, " +
                     "macaddress, createtime) values(?,?,?,?,?,?,?)";
        try{
            conn = DBConnection.getConnection();
            ps = conn.prepareStatement(sql);
            //优化插入第一步设置手动提交  
            conn.setAutoCommit(false); 
            int len = list.size();
            for(int i=0; i<len; i++) {
                ps.setString(1, list.get(i).getGuid());
                ps.setString(2, list.get(i).getDeviceBrand());
                ps.setString(3, list.get(i).getDeviceName());
                ps.setString(4, list.get(i).getDeviceIp());
                ps.setString(5, list.get(i).getIpAddress());
                ps.setString(6, list.get(i).getMacAddress());
                ps.setString(7, list.get(i).getCreateTime());
                //if(ps.executeUpdate() != 1) r = false;    优化后,不用传统的插入方法了。
                //优化插入第二步插入代码打包,等一定量后再一起插入。
                ps.addBatch(); 
                //if(ps.executeUpdate() != 1)result = false;
                //每200次提交一次 
                if((i!=0 && i%200==0) || i==len-1){//可以设置不同的大小;如50,100,200,500,1000等等  
                    ps.executeBatch();  
                    //优化插入第三步提交,批量插入数据库中。
                    conn.commit();  
                    ps.clearBatch();//提交后,Batch清空。
                }
            }
        } catch (Exception e) {
            System.out.println("MibTaskPack->getArpInfoList() error:" + e.getMessage());
            return false;   //出错才报false
        } finally {
            DBConnection.closeConection(conn, ps, rs);
        }
        return true;
    }

三、通过多线程执行jdbc过程

1、链接

http://t.zoukankan.com/fangts-p-6813515.html

2、代码

package tenThreadInsert;
 
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.Date;
 
public class MyThread extends Thread{
                public void run() {
                     String url = "jdbc:mysql://127.0.0.1/teacher"; 
                     String name = "com.mysql.jdbc.Driver"; 
                     String user = "root"; 
                     String password = "123456"; 
                    Connection conn = null; 
                    try {
                        Class.forName(name);
                        conn = DriverManager.getConnection(url, user, password);//获取连接 
                        conn.setAutoCommit(false);//关闭自动提交,不然conn.commit()运行到这句会报错
                    } catch (ClassNotFoundException e1) {
                        e1.printStackTrace();
                    } catch (SQLException e) {
                        e.printStackTrace();
                    }
                // 开始时间
                Long begin = new Date().getTime();
                // sql前缀
                String prefix = "INSERT INTO test_teacher (t_name,t_password,sex,description,pic_url,school_name,regist_date,remark) VALUES ";
                try {
                    // 保存sql后缀
                    StringBuffer suffix = new StringBuffer();
                    // 设置事务为非自动提交
                    conn.setAutoCommit(false);
                    // 比起st,pst会更好些
                    PreparedStatement  pst = (PreparedStatement) conn.prepareStatement("");//准备执行语句
                    // 外层循环,总提交事务次数
                    for (int i = 1; i <= 10; i++) {
                        suffix = new StringBuffer();
                        // 第j次提交步长
                        for (int j = 1; j <= 100000; j++) {
                            // 构建SQL后缀
                            suffix.append("('" +i*j+"','123456'"+ ",'男'"+",'教师'"+",'www.bbb.com'"+",'Java大学'"+",'"+"2016-08-16 14:43:26"+"','备注'" +"),");
                        }
                        // 构建完整SQL
                        String sql = prefix + suffix.substring(0, suffix.length() - 1);
                        // 添加执行SQL
                        pst.addBatch(sql);
                        // 执行操作
                        pst.executeBatch();
                        // 提交事务
                        conn.commit();
                        // 清空上一次添加的数据
                        suffix = new StringBuffer();
                    }
                    // 头等连接
                    pst.close();
                    conn.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
                // 结束时间
                Long end = new Date().getTime();
                // 耗时
                System.out.println("100万条数据插入花费时间 : " + (end - begin) / 1000 + " s"+"  插入完成");
    }  
}
 测试代码

package tenThreadInsert;
 
public class Test {
 
    public static void main(String[] args) {
        for (int i = 1; i <=10; i++) {
              new MyThread().start();
            }
    }
 
}

四、一次性插入多条记录

1、原理

MySQL:

INSERT INTO Persons (LastName, Address) VALUES ('Wilson', 'Champs-Elysees'),('Gates', 'Champs-Elysees')

Oracle:

insert into 表名 (字段1)

select '1' from dual

union all

select '2' from dual

2、代码

(1)调用

public static Boolean insertManyByOne(int num) {
        String sql = GenSqlUtil.genInsManySql(num);
        // System.out.println(sql);
        jdbcUtils.insertMany(sql);
        System.out.println("共插入" + num + "条数据");
        return true;
    }


public static String genInsManySql(int num) {
        String sql = "INSERT INTO TEST.\"ABANK\"\n ";
        for (int i = 0; i < num; i++) {
            sql = sql.concat("select '1', 'CH', '9999', 'Zürcher Kantonalbank', " +
                    "'ZKBKCHZZ80A', ' ', TO_DATE('2009-11-10 00:00:00', 'SYYYY-MM-DD HH24:MI:SS'), " +
                    "TO_DATE('1599-12-31 00:00:00', 'SYYYY-MM-DD HH24:MI:SS'), " +
                    "TO_DATE('2017-07-12 00:00:00', 'SYYYY-MM-DD HH24:MI:SS'), " +
                    "'ADMIN', TO_DATE('1599-12-31 00:00:00', 'SYYYY-MM-DD HH24:MI:SS'), " +
                    "'ADMIN', TO_TIMESTAMP('2021-04-23 08:54:05.000', 'SYYYY-MM-DD HH24:MI:SS:FF3'), " +
                    "TO_TIMESTAMP('"+ dateFormat.format(calendar.getTime()) +
                    "', 'SYYYY-MM-DD HH24:MI:SS:FF3'), " +
                    "HEXTORAW('"+ RandNumGenUtil.genDefLenStr(15) +"') from dual");
            if (i != num -1) {
                sql = sql.concat("\n union all \n");
            }
        }
        return sql;
    }

(2)jdbcutils

package com.boulderaitech.utils;

import java.sql.*;
import java.util.Arrays;

public class JDBCUtil {
    private String user;
    private String pass;
    private String url;

    private Connection conn = null;//连接对象
    private ResultSet rs = null;//结果集对象
    private Statement sm = null;

    /**
     * 构造函数获得数据库用户名和密码
     *
     * @param user
     * @param pass
     */
    public JDBCUtil(String user, String pass) {
        this.user = user;
        this.pass = pass;
        this.url = "jdbc:oracle:thin:@//172.16.5.162:1521/helowin";
    }

    /**
     * 连接数据库
     *
     * @return
     */
    public Connection createConnection() {
        String sDBDriver = "oracle.jdbc.driver.OracleDriver";
        try {
            Class.forName(sDBDriver).newInstance();
            conn = DriverManager.getConnection(url, user, pass);
        } catch (Exception e) {
            System.out.println("数据库连接失败");
            e.printStackTrace();
        }
        return conn;
    }

    /**
     * 关闭数据库
     *
     * @param conn
     */
    public void closeConnection(Connection conn) {
        try {
            if (conn != null) {
                conn.close();
            }
        } catch (Exception e) {
            System.out.println("数据库关闭失败");
            e.printStackTrace();
        }
    }

    /**
     * 插入数据
     *
     * @param insert 插入语句
     * @return
     */
    public int insert(String insert) {
        conn = createConnection();
        //String insert = "insert into t_department values('D004','金融部')";
        int re = 0;
        try {
            conn.setAutoCommit(false);//事物开始

            sm = conn.createStatement();
            re = sm.executeUpdate(insert);
            if (re < 0) {               //插入失败
                conn.rollback();      //回滚
                sm.close();
                closeConnection(conn);
                return re;
            }
            conn.commit();            //插入正常
            sm.close();
            closeConnection(conn);
            return re;
        } catch (Exception e) {
            e.printStackTrace();
        }
        closeConnection(conn);
        return 0;
    }

    /**
     * 批量插入数据
     */
    public int insertBatch(String[] sql) {
        conn = createConnection();
        //String insert = "insert into t_department values('D004','金融部')";
        int re = 0;
        try {
            conn.setAutoCommit(false);//事务开始
            sm = conn.createStatement();
            Arrays.stream(sql).forEach(x->{
                try {
                    sm.executeUpdate(x);
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            });
            conn.commit();            //插入正常
            sm.close();
            closeConnection(conn);
            return re;
        } catch (Exception e) {
            e.printStackTrace();
        }
        closeConnection(conn);
        return 0;
    }

    /**
     * 查询语句
     * 返回结果集
     *
     * @param select
     * @return
     */
    public ResultSet selectSql(String select) {
        conn = createConnection();
        try {
            sm = conn.createStatement();
            rs = sm.executeQuery(select);
            return rs;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 根据结果集输出
     *
     * @param rs
     */
    public void printRs(ResultSet rs) {
        int columnsCount = 0;
        boolean f = false;
        try {
            if (!rs.next()) {
                return;
            }
            ResultSetMetaData rsmd = rs.getMetaData();
            columnsCount = rsmd.getColumnCount();//数据集的列数
            for (int i = 0; i < columnsCount; i++) {
                System.out.print(rsmd.getColumnLabel(i + 1) + "/n"); //输出列名
            }
            System.out.println();

            while (!f) {
                for (int i = 1; i <= columnsCount; i++) {
                    //System.out.print(rs.getString(i)+"/t");
                    //逻辑处理
                    String name = rs.getString("NAME");

                    System.out.print(rs.getString("NAME") + "/n");
                }
                System.out.println();
                if (!rs.next()) {
                    f = true;
                }
            }
            rs.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
        closeConnection(conn);
    }

    /**
     * 插入数据
     *
     * @param update 更新语句
     * @return
     */
    public int update(String update) {
        conn = createConnection();
        //String insert = "insert into t_department values('D004','金融部')";
        int re = 0;
        try {
            conn.setAutoCommit(false);//事物开始

            sm = conn.createStatement();
            re = sm.executeUpdate(update);
            if (re < 0) {               //插入失败
                conn.rollback();      //回滚
                sm.close();
                closeConnection(conn);
                return re;
            }
            conn.commit();            //插入正常
            sm.close();
            closeConnection(conn);
            return re;
        } catch (Exception e) {
            e.printStackTrace();
        }
        closeConnection(conn);
        return 0;
    }

    public int insertMany(String sql) {
        conn = createConnection();
        int re = 0;
        try {
            conn.setAutoCommit(false);//事物开始

            sm = conn.createStatement();
            re = sm.executeUpdate(sql);
            if (re < 0) {               //插入失败
                conn.rollback();      //回滚
                sm.close();
                closeConnection(conn);
                return re;
            }
            conn.commit();            //插入正常
            sm.close();
            closeConnection(conn);
            return re;
        } catch (Exception e) {
            e.printStackTrace();
        }
        closeConnection(conn);
        return 0;
    }
}

五、通过定时器实现定时执行

public static Boolean insertBatchFixTime(int numOfInsert, int timePerEpoch) {
        Timer timer = new Timer();
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                insertManyByOne(numOfInsert);
            }
        }, 0L, timePerEpoch * 1000L);
        System.out.println("当前线程:" + Thread.currentThread().getName() + " 当前时间" + LocalDateTime.now());
        return true;
    }

六、通过循环实现批量插入

public static Boolean insertBatchFixCircle(int numOfEachInsert, int numOfEpoch) {
        LocalDateTime start = LocalDateTime.now();
        for (int i = 0; i < numOfEpoch; i++) {
            insertManyByOne(numOfEachInsert);
        }
        System.out.println("共插入" + numOfEachInsert * numOfEpoch+"条数据");
        LocalDateTime end = LocalDateTime.now();
        System.out.println("共耗时" + Duration.between(start, end).toMillis() + "ms");
        return true;
    }
<think>好的,我现在需要帮助用户将数据库中的大量数据集成到AnythingLLM的知识库中。首先,用户之前已经询问过如何将数据库数据作为知识库使用,现在的问题是针对“大量数据”的情况。我需要考虑用户可能的深层需求,比如处理大数据时的效率、自动化以及系统资源的问题。 首先,用户提到“大量数据”,这可能意味着他们之前的尝试遇到了性能问题,或者他们预计数据量大会影响导入速度和处理能力。因此,我需要详细说明如何处理大数据,可能包括分批次处理、优化数据预处理和使用高效的工具。 接下来,我应该回顾之前的回答,确保这次的建议在之前的基础上扩展,特别是针对数据量大的挑战。之前的步骤包括导出数据、预处理、导入和自动化集成。现在需要针对每个步骤在大数据情况下的调整。 在导出数据部分,需要考虑如何高效地从数据库中导出大量数据。可能需要建议使用数据库批量导出工具或命令行工具,比如MySQL的mysqldump或者PostgreSQL的pg_dump,但要注意导出为适合处理的格式,如CSV或JSON。同时,可能需要分表或分批次导出,避免单次操作数据量过大。 预处理阶段是关键,尤其是数据量大时。需要强调结构化转文本的效率,可能建议使用脚本自动化处理,比如用Python的Pandas库处理CSV文件,或者用jq处理JSON。此外,分块处理很重要,要确保文本在导入前已经适当分块,减少AnythingLLM的处理压力。还要注意清理无用字段,减少数据体积。 在导入到AnythingLLM时,需要分批次上传,避免一次性上传导致内存不足或超时。建议用户使用命令行工具或自动化脚本,结合定时任务(如cron)来分批处理。同时,利用AnythingLLM的API进行自动化上传,可能还需要处理可能的API限流问题,比如设置延迟 between请求。 自动化集成方面,用户可能需要一个可持续的解决方案,尤其是数据库经常更新的情况下。建议使用ETL工具或自定义脚本,定期同步增量数据,而不仅仅是全量导入。此外,监控和日志记录也很重要,确保在大量数据处理过程中能够跟踪进度和诊断问题。 注意事项部分需要强调资源监控,比如CPU、内存和存储的使用情况,建议用户扩展硬件资源或使用云服务。另外,测试环节不可忽视,先在子集上验证流程,再扩展到全部数据,避免中途失败导致重复劳动。 最后,用户可能有隐藏的担忧,比如处理时间过长或系统稳定性。因此,需要提供优化建议,如调整AnythingLLM的chunk大小和重叠参数,使用分布式处理,或者联系技术支持寻求帮助。这些建议可以帮助用户更高效地处理大量数据,确保集成顺利。</think>将海量数据库数据集成到 **AnythingLLM** 的知识库中时,需特别关注 **数据规模、处理效率** 和 **系统资源管理**。以下是针对大规模数据的优化方案和步骤: -- ### **一、数据导出的高效策略** #### 1. **分批次导出** - **分表/分库导出**:若单表数据量过大,按时间范围、主键区间(如 `WHERE id BETWEEN 1000 AND 2000`)分批提取。 - **工具辅助**: - 使用数据库原生工具(如 MySQL 的 `mysqldump`、PostgreSQL 的 `pg_dump`)导出为 CSV/JSON。 - 对 NoSQL(如 MongoDB)使用 `mongoexport` 分批次导出集合数据。 #### 2. **格式选择** - **列式存储**:优先导出为 **Parquet** 格式(高效压缩,适合大规模数据)。 - **流式传输**:通过管道(Pipe)直接导出到处理脚本,避免中间文件占满磁盘。 --- ### **二、数据预处理优化** #### 1. **结构化转文本的自动化** - **脚本批量处理**: - 用 Python(Pandas)或 Go 编写转换脚本,将每条记录拼接为自然语言文本。 *示例*: ```python # 读取 CSV 并生成文本 import pandas as pd df = pd.read_csv("data.csv") df["text"] = df.apply(lambda row: f"产品:{row['name']},价格:{row['price']}元。描述:{row['desc']}", axis=1) df["text"].to_csv("output.txt", index=False) ``` - **并行处理**:利用多线程/多进程加速(如 Python 的 `multiprocessing` 库)。 #### 2. **高效分块** - **预处理分块**:在导入前按语义分割文本(如每 500 字一段),减少 AnythingLLM 的默认分块压力。 - **工具推荐**:使用 `LangChain` 的 `RecursiveCharacterTextSplitter` 提前分块。 #### 3. **数据清洗** - 删除冗余字段(如日志时间戳、无关 ID)。 - 过滤低质量数据(如空值记录、重复内容)。 --- ### **三、导入 AnythingLLM 的规模化方案** #### 1. **分批次上传** - 将预处理后的文本拆分为多个文件(如 `data_part1.txt`, `data_part2.txt`),通过 AnythingLLM 界面分批上传。 - **命令行工具(推荐)**: 若 AnythingLLM 支持 API,编写脚本调用 `/upload` 接口分批次上传: ```bash # 示例:用 curl 上传(需替换 API_KEY 和文件路径) curl -H "Authorization: Bearer API_KEY" -F "file=@data_part1.txt" http://localhost:3001/upload ``` #### 2. **资源监控** - **硬件扩展**:为 AnythingLLM 部署的服务器增加内存(建议 ≥16GB)和 SSD 磁盘。 - **向量数据库优化**: - 若使用内置 LanceDB,调整 `chunk_size`(如 512 tokens)和 `chunk_overlap`(如 50 tokens)。 - 外部向量库(如 Pinecone)可支持更大规模数据。 #### 3. **增量同步** - 通过数据库的 **Binlog 或 Change Stream** 监听新增数据,实时导出并预处理。 - 定期调用 AnythingLLM API 更新知识库,避免全量重复导入。 -- ### **四、自动化与运维** #### 1. **流水线设计** ```mermaid graph LR A[数据库] --> B[批量导出工具] B --> C[预处理脚本] C --> D[分块/清洗] D --> E[AnythingLLM 分批次导入] E --> F[监控报警] ``` #### 2. **工具链推荐** - **调度工具**:Apache Airflow 或 Prefect 管理定时任务。 - **错误处理**:记录失败批次并自动重试。 - **日志追踪**:记录每个批次的处理时间和资源消耗。 --- ### **五、注意事项** 1. **性能瓶颈**: - 导入时观察 CPU/内存占用,若持续 100% 需扩大分块粒度或升级硬件。 2. **测试验证**: - 先用 1% 的数据验证流程,再全量运行。 3. **法律合规**: - 若含用户隐私数据,需在预处理阶段脱敏(如替换手机号、邮箱)。 -- ### **附:优化案例** - **场景**:某电商需导入 100 万条产品数据。 - **方案**: 1. 按产品类目分 20 个 CSV 导出。 2. 用 Pandas 并行生成文本,分块为 5000 个 TXT 文件。 3. 通过 AnythingLLM API 每小时上传 200 个文件,总耗时 25 小时。 4. 最终知识库响应速度保持在 2 秒内。 --- 通过以上方法,可平衡效率与稳定性,实现大规模数据库与 AnythingLLM 的高效集成。如果遇到具体技术问题(如 API 限流),建议结合日志具体分析。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值