最有效、零成本的吞吐量提升方式是单条insert多值合并,因其将n次sql解析、权限校验、日志刷盘和网络往返压缩为1次;但必须配合显式事务(set autocommit=0或start transaction)才能释放全部性能,否则仅提升30%~50%。

直接合并为 INSERT INTO t(col1,col2) VALUES (v1,v2),(v3,v4),(v5,v6) 是最有效、零成本的吞吐量提升方式——它把 N 次 SQL 解析、权限校验、日志刷盘和网络往返压成 1 次。
为什么单条 INSERT 慢得明显
不是语句本身慢,是默认行为在“重复劳动”:每条 INSERT 都要走完整流程——客户端发包 → 服务端解析语法 → 检查权限 → 加锁 → 写 redo + binlog → 刷盘 → 返回 OK。1000 条就是 1000 套流程。即使数据量小,Packet too large 不报错,query end 状态卡住、Innodb_log_waits > 0 这些监控指标也会暴露 I/O 已经打满。
合并时必须注意的字段与类型约束
看起来只是多加几对括号,但 MySQL 对 VALUES 列表内部一致性很敏感:
- 所有行的字段顺序必须严格一致,
INSERT INTO t(x,y) VALUES (1,'a'),(NULL,2)可能被某些驱动判定为类型不匹配(第二行 y 是数字,但第一行 y 是字符串) - 避免混用
NULL和非空值在同一批的相同列位置,尤其当该列有默认值或触发器时,隐式转换可能绕过批量优化路径 - 含
TEXT、JSON或长字符串的行会显著增大单行体积,建议先估算:max_allowed_packet默认 4MB,减去 SQL 模板开销后,每行平均占 200 字节 → 理论上限约 2 万行,但生产建议控制在 1000–5000 行
事务包裹不是可选项,而是必要动作
只合并 SQL 不关 autocommit,性能只能提升 30%~50%;加上 START TRANSACTION + COMMIT 才能释放全部潜力:
- 确认当前状态:
SELECT @@autocommit;—— 大多数环境返回 1 - 插入前执行:
SET autocommit = 0;或显式写START TRANSACTION; - 每批 COMMIT 行数建议 1000–5000 行:太少起不到事务聚合效果;太多易触发
innodb_lock_wait_timeout或阻塞其他业务 - 别依赖 ORM 的
@Transactional注解——纯数据导入场景下,它通常不生效
真正拖慢速度的,往往是看不见的后台动作
你看到的是 SQL 合并了,但 InnoDB 仍在后台逐行做这些事:
- 唯一索引校验不会跳过:如果主键或唯一键是随机 UUID,B+ 树会频繁分裂,吞吐量断崖下跌
- 外键和唯一性检查可临时关闭:
SET FOREIGN_KEY_CHECKS = 0;和SET UNIQUE_CHECKS = 0;,导入完立刻设回 1 -
ALTER TABLE t DISABLE KEYS对 MyISAM 有效,但对 InnoDB 无效——InnoDB 没有这个机制,别白费力气 - 主键尽量按升序插入:提前排序源数据(如按自增 ID 或时间戳),能让聚簇索引追加写入,减少 page fault
最常被跳过的一步是验证 max_allowed_packet 实际值和调整 innodb_flush_log_at_trx_commit。不查就调参,往往改了也没用;而设成 0 虽快,但断电会丢最多 1 秒数据——仅限离线导入,切勿用于线上实时写入。











