大批量INSERT越插越慢是因为每条语句默认触发索引更新和redo log刷盘,索引越多、事务越小,随机写放大越严重;批量插入、调优参数、延迟建索引及使用LOAD DATA可显著提升性能。

大批量 INSERT 时为什么越插越慢?
因为每条 INSERT 默认触发一次索引更新 + redo log 刷盘,索引越多、事务越小,开销越爆炸。尤其当表有多个二级索引时,单行插入可能引发数倍的随机写放大。
- 用
INSERT INTO ... VALUES (...), (...), (...)批量写入,100 行一包比 100 次单行快 5–10 倍(取决于网络延迟和 innodb_buffer_pool_size) - 避免在循环里拼接大 SQL 字符串——PHP/Python 中字符串反复 realloc 容易 OOM;改用预编译 +
executemany()或分段组装 - 如果主键是自增
INT,别用REPLACE INTO或INSERT ... ON DUPLICATE KEY UPDATE代替普通插入——它们会先查再写,锁更重、冲突更多
innodb_flush_log_at_trx_commit=2 真的安全吗?
设成 2 后,事务提交只写入 OS cache,不强制 fsync() 到磁盘,崩溃时最多丢 1 秒数据。对日志类、埋点类、非金融场景,这是性价比最高的折中。
- 必须配合
sync_binlog=0或sync_binlog=1000(否则 binlog 和 redo 不一致,主从可能出错) - 不要在 RDS 上擅自改这个值——阿里云/AWS 的 MySQL 实例默认已调优,改了反而触发平台告警或被自动重置
-
innodb_doublewrite=ON必须保持开启,2不影响 doublewrite 保护,但关了它等于裸奔
延迟创建索引真的能提速?
能,但只在「先灌数据、再建索引」的离线导入场景有效。MySQL 5.7+ 的 ALTER TABLE ... ALGORITHM=INPLACE 对二级索引添加已优化,但唯一约束仍需全表扫描校验。
- 导入前用
ALTER TABLE t DROP INDEX idx_a, DROP INDEX idx_b,导入完再ADD INDEX,速度可提升 3–8 倍(尤其 SSD 上) - 别对主键或唯一索引这么做——
DROP PRIMARY KEY会重建整表,比边插边建还慢 - 注意:如果导入过程中有其他业务查询该表,删索引会导致全表扫描变多,可能拖垮 QPS
LOAD DATA INFILE 被拒怎么办?
常见于权限不足、secure_file_priv 限制或字段格式不匹配。它比任何 SQL 插入都快,因为绕过 SQL 解析层,直接走存储引擎接口。
- 检查
SELECT @@secure_file_priv返回路径,CSV 文件必须放那里;若为空,需重启 mysqld 加--secure-file-priv=/tmp - 用
FIELDS TERMINATED BY ',' ENCLOSED BY '"'显式声明格式,否则遇到含逗号的文本字段会错位 - 如果表有自增主键,CSV 第列留空或填
0,MySQL 会自动补;但千万别填NULL字符串,会被当字面量插入导致重复键错误


















