批量插入性能远超单条INSERT,关键在于合理划分事务边界、禁用约束检查、使用LOAD DATA INFILE、规避自增主键热点、优化连接池配置,并优先排查锁与日志刷盘瓶颈。

批量插入比单条 INSERT 快得多,但要注意事务边界
单条 INSERT INTO t VALUES (1),(2) 和 1000 条 INSERT 语句在高并发下性能差距可达 10 倍以上。关键不是“能不能用批量”,而是“在哪切分事务”。
MySQL 的 autocommit=1 默认开启,每条 INSERT 都触发一次刷盘(innodb_flush_log_at_trx_commit=1 时),这是最大瓶颈。
- 用
INSERT INTO t VALUES (1),(2),(3),...,(1000)替代循环执行 1000 次单值INSERT - 显式开启事务:
BEGIN; INSERT ... ; INSERT ... ; COMMIT;,把 100–1000 行包在一个事务里(太大易锁表、回滚开销高) - 临时调大
innodb_log_file_size和innodb_buffer_pool_size,避免频繁 checkpoint - 写入前禁用唯一索引和外键检查(仅限导入场景):
SET unique_checks=0; SET foreign_key_checks=0;,完成后恢复
LOAD DATA INFILE 是最快写入方式,但权限和路径限制多
比等量 INSERT 快 5–20 倍,本质是绕过 SQL 解析层,直接走存储引擎接口。但它要求数据文件在 MySQL 服务端本地(或启用了 local_infile=ON 并配合客户端 --local-infile),且用户需有 FILE 权限。
- 格式必须严格:字段分隔符(
FIELDS TERMINATED BY ',')、行结束符(LINES TERMINATED BY '\n')、空值表示(NULL或\N)都要匹配 - 避免使用
REPLACE或IGNORE,它们会触发行锁或唯一键扫描;如需去重,先写临时表再INSERT ... SELECT ... ON DUPLICATE KEY UPDATE - 大文件分片导入更稳:
split -l 100000 data.csv chunk_,再逐个LOAD DATA INFILE '/path/chunk_aa' - 导入后立刻执行
ANALYZE TABLE t,更新统计信息,避免后续查询执行计划劣化
高并发写入时,自增主键冲突和热点页争用要主动规避
InnoDB 的聚簇索引让主键成为写入热点。若所有连接都往同一数据页末尾插入(比如 AUTO_INCREMENT 主键),会产生 LOCK_MODE_X 行锁竞争,甚至 adaptive hash index 锁等待。
- 用
innodb_autoinc_lock_mode=2(交错模式),允许并发获取自增值(需 binlog_format=ROW) - 避免
REPLACE INTO或INSERT ... ON DUPLICATE KEY UPDATE在高频写入场景滥用——它们先查后写,容易锁住唯一索引项 - 考虑用
UUID_SHORT()或雪花 ID(应用层生成)替代AUTO_INCREMENT,但注意 UUID 无序性会导致二级索引碎片,需定期OPTIMIZE TABLE - 写多读少场景可加
innodb_change_buffering=inserts(默认已开),延迟合并非唯一二级索引变更,降低随机 IO
连接池配置不当会让优化全失效
哪怕 SQL 和参数全调优了,如果应用层用短连接(每次请求新建/销毁连接),或连接池最大数设成 1000,照样卡在 Creating sort index 或 Waiting for table metadata lock。
- 连接池
maxActive/maxPoolSize不应盲目设高;建议按实际并发请求数 × 1.5 设(如 QPS 200,平均响应 100ms,则活跃连接约 20) - 务必启用
wait_timeout和interactive_timeout(建议 300 秒),防止空闲连接长期占着不放 - 用
SHOW PROCESSLIST查看是否有大量Sleep状态连接;用performance_schema.events_statements_summary_by_digest找出慢写入的 SQL 模板 - 写入类请求尽量走专用连接池(和读库分离),避免慢写拖垮读响应
真正卡住高并发写的,往往不是磁盘或 CPU,而是锁等待、日志刷盘节奏、或者连接没被复用。调参前先看 SHOW ENGINE INNODB STATUS\G 里的 SEMAPHORES 和 TRANSACTIONS 部分,比盲目改 innodb_buffer_pool_size 有用得多。











