批量插入1000万条数据的三种方式对比

重要的写在前面的。如果你有更好的方法。希望您不吝啬您的留言。共同进步

一、直接导入
-- 查看允许mysql数据导出目录
SHOW VARIABLES LIKE "secure_file_priv";
SELECT * FROM demo INTO OUTFILE 'D:/up/prod.txt'
    FIELDS TERMINATED BY '\,' -- 字段的分隔符 这里是逗号
        OPTIONALLY ENCLOSED BY '\"'     -- 遇到像char varchar等类型的加上双引号
    LINES

        TERMINATED BY '\r\n'; -- 每条数据之后的处理方式 -- 3.353s


更多参数详情请查看mysql的API。

将文件存储到本地磁盘txt.然后通过mysql自带的导入向导导入数据。    -- 620.922s

二、存储过程
--  存储过程循环拼接,成一条insert。并且最好设置一下每一千条提交一次(都有利于提高插入速度)-- 256
CREATE PROCEDURE sp2(IN i INT)
BEGIN
-- set something
DECLARE a,b INT;
DECLARE  v_start_time  datetime;
  SET v_start_time  :=now();
SET a :=0;
SET b := i;
set @exesql = concat("insert into demo(`value`) values ");
set @exedata = "";
-- do something
-- DELETE FROM demo WHERE `key` > a;
WHILE a < b DO
SET @exedata = concat(@exedata, ",('", a, "')");
SET a := a+1;
IF a%1000=0 THEN-- 1000条插入一次
set @exedata = SUBSTRING(@exedata, 2);
set @exesql = concat("insert into demo(`value`) values ", @exedata);
prepare stmt from @exesql;
execute stmt;
DEALLOCATE prepare stmt;
set @exedata = "";
COMMIT;
END IF;
-- INSERT INTO demo(`value`) VALUES(a);
-- SET a := a+1;
END WHILE;
IF length(@exedata)>0 THEN
set @exedata = SUBSTRING(@exedata, 2);
set @exesql = concat("insert into demo(`value`) values ", @exedata);
prepare stmt from @exesql;
execute stmt;
DEALLOCATE prepare stmt;
set @exedata = "";
COMMIT;
END IF;
SELECT concat('耗时:', unix_timestamp(now()) - unix_timestamp(v_start_time), '秒!');
END

三、LOAD DATA INFILE
-- LOAD DATA INFILE语句用于高速地从一个文本文件中读取行,并装入一个表中。文件名称必须为一个文字字符串。
load data local infile "D:/up/prod.txt" into table demo 
FIELDS TERMINATED BY '\,'
OPTIONALLY ENCLOSED BY '\"'
LINES TERMINATED BY '\r\n'; -- 29.611s
更多参数详情请查看mysql的API。


-- 总结
1、INSERT INTO workinfo (column3, column13) VALUES (1, 2), (2, 3),(4,5),这样可以减少一些INSERT语句
2、可以考虑用LOAD DATA INFILE
3、适当调大bulk_insert_buffer_size参数
4、插入前执行锁表操作LOCK TABLES workinfo WRITE,插入完成后UNLOCK TABLES
5、插入数据前ALTER TABLE workinfo DISABLE KEYS避免更新索引结构,插入完成后再ALTER TABLE workinfo ENABLE KEYS
6、并行插入,不过推荐多线程并行插入
7、存储引擎MyISAM对于大批量数据的INSERT更快点

<think>嗯,用户问怎么优化往数据插入1000万条数据的操作。这个问题挺常见的,但处理大数据插入确实需要仔细考虑。首先,我得回忆一下常见的优化手段,然后逐步分析每个可能的优化点。 首先,用户可能是在做数据迁移或者初始化大量数据,比如测试环境的数据填充。这时候,单条插入肯定不行,因为每次插入都有网络开销和事务日志的开销。所以,第一个想到的是批量插入。比如,用INSERT INTO ... VALUES (),(),()的方式,一次性插入多条记录。这样可以减少数据库的交互次数,提升效率。不过,批量的大小需要合适,不能太大,否则可能会超出数据库的限制或者内存问题。通常,每批1000条左右比较合适,但具体还得看数据库和配置。 然后,事务的处理。如果每条插入都自动提交,那会有很大的性能损耗。所以,应该把多个批量操作放在一个事务里提交,减少事务提交的次数。不过,事务太大也可能导致日志文件增长过快,或者锁的问题,需要平衡。 接下来,预处理语句,比如使用PreparedStatement,这样可以避免SQL解析的开销,数据库可以缓存执行计划,提高效率。特别是在循环中重复执行相似的SQL时,预处理能明显提升速度。 还有,调整数据库的配置参数。比如,增大innodb_buffer_pool_size(如果是MySQL的话),让更多的数据在内存中处理,减少磁盘IO。另外,关闭自动提交,设置unique_checks和foreign_key_checks为0,插入完成后再恢复,这些都能减少检查的开销。不过需要注意,关闭外键检查可能会有数据不一致的风险,所以得确保数据本身没有问题。 然后,考虑禁用索引。在插入大量数据时,索引的维护会占用很多时间。可以先删除索引,插入数据后再重建索引。特别是聚集索引,如果主键是自增的,按顺序插入可以减少页分裂,提升速度。 数据加载工具也是一个点。像MySQL的LOAD DATA INFILE,或者PostgreSQL的COPY命令,这些工具比普通的INSERT快很多,因为它们是专门为批量数据导入设计的,直接读写文件,效率更高。 并行插入的话,如果数据库支持,可以多线程或者分批次处理,但要注意避免锁竞争。比如,按主键范围分成多个任务,同时插入不同的分区,不过需要系统资源足够,比如CPU、磁盘IO不能成为瓶颈。 另外,表设计方面,如果字段很多,可以考虑拆分,先插入必要的字段,后续再更新其他字段。或者使用更紧凑的数据类型,减少存储空间,提高处理速度。 硬件方面,如果可能的话,提升磁盘性能,比如使用SSD,增加内存,或者优化数据库服务器的配置,比如调整日志文件的存放位置,使用更快的存储设备。 还有,在插入前进行数据预处理,比如去重、格式化,避免在插入过程中因为数据错误导致回滚或者中断。预处理好的数据能更顺畅地插入。 最后,测试和监控很重要。先用小批量数据测试方案的有效性,观察数据库的负载情况,比如CPU、内存、IO的使用率,根据实际情况调整参数。比如,如果发现磁盘IO很高,可能需要调整缓冲区大小或者优化磁盘配置。 不过,用户可能有特定的场景,比如使用的数据库类型不同(MySQL、PostgreSQL等),或者数据是否已经预处理好,是否有网络传输的延迟(比如远程数据库)。这些因素都会影响优化的策略。需要根据具体情况选择合适的优化方法,有时候需要结合多种策略才能达到最佳效果。</think>以下是分步骤优化千万级数据插入的完整方案: 一、基础优化阶段(3-5倍提升) 1. 批量插入优化 - 使用批量插入语句:$$INSERT INTO table (col1,col2) VALUES (v1,v2),(v3,v4)...$$ - 推荐每批500-1000条(需测试确定最佳值) - 示例代码(Java): ```java String sql = "INSERT INTO table (a,b) VALUES (?,?)"; PreparedStatement ps = conn.prepareStatement(sql); for (int i=0; i<1e7; i++) { ps.setInt(1, value1); ps.setInt(2, value2); ps.addBatch(); if(i%1000 == 0) { ps.executeBatch(); conn.commit(); } } ``` 2. 事务优化 - 合并多个批量操作为一个事务 - 设置`autocommit=0`后手动提交 二、中级优化(5-10倍提升) 3. 参数调优 ```sql SET unique_checks = 0; -- 禁用唯一约束检查 SET foreign_key_checks = 0; -- 禁用外键检查 SET sql_log_bin = 0; -- 非主从环境禁用二进制日志 ``` 4. 索引优化 ```sql ALTER TABLE table_name DROP INDEX index_name; -- 插入前删除非主键索引 -- 插入数据... ALTER TABLE table_name ADD INDEX index_name(column); -- 完成后重建 ``` 三、高级优化(10倍+提升) 5. 使用专用加载工具 - MySQL示例: ```sql LOAD DATA INFILE '/path/file.csv' INTO TABLE table FIELDS TERMINATED BY ',' LINES TERMINATED BY '\n'; ``` 6. 表结构优化 - 使用`TABLESPACE`分离数据与索引存储 - 调整`ROW_FORMAT=COMPRESSED` 四、分布式方案(适用于超大规模) 7. 分库分表插入 - 按哈希值分10个线程并行插入 - 每个线程处理100万条数据 - 使用连接池管理数据库连接 五、配套优化措施 8. 服务器参数调整 ```ini # MySQL my.cnf 配置 innodb_flush_log_at_trx_commit = 0 innodb_buffer_pool_size = 80%内存 bulk_insert_buffer_size = 256M ``` 9. 硬件优化 - 使用SSD固态硬盘 - 确保服务器内存≥32GB - 配置RAID 10磁盘阵列 六、实施注意事项 1. 始终保留数据库备份 2. 在测试环境验证方案 3. 监控服务器资源(CPU/内存/IO) 4. 分阶段提交(每50万条做一次checkpoint) 5. 完成后执行`ANALYZE TABLE`更新统计信息 典型性能对比: - 原始逐条插入:约 1000条/秒 - 优化后方案:可达 50000条/秒 - 总耗时从2.7小时降至约5分钟 建议先使用`EXPLAIN`分析执行计划,通过小批量测试(如1万条)验证优化效果后再进行全量操作。不同数据库(Oracle/PostgreSQL/SQL Server)需调整具体参数,但核心原理相通。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值