高并发批量插入性能优化需从事务设计、SQL组织、索引结构和执行粒度四方面协同:缩小事务范围、启用JDBC批处理、规避唯一索引隐式锁、精简非必要索引。

高并发下批量插入数据库时,锁竞争是性能下降和死锁的主因。核心思路不是“怎么加锁”,而是“怎么少锁、快锁、不争锁”。关键在事务设计、SQL组织、索引结构和执行粒度四个层面协同优化。
缩小事务范围,避免长事务持锁
大事务虽减少提交次数,但会延长行锁/间隙锁持有时间,尤其在 可重复读(RR)隔离级别 下易引发间隙锁冲突。例如插入带唯一索引的字段时,MySQL 会对插入位置前后加 gap lock,多线程并发插入相邻值极易互相阻塞。
- 将单个万级大事务拆为多个千级小事务(如每 500 条提交一次),既控制锁持有时间,又避免单次事务回滚代价过大
- 避免在事务内执行耗时操作(如远程调用、复杂计算),防止锁被无谓占用
- 确认业务是否真需要强一致性:若允许短暂最终一致,可考虑先写入临时表或消息队列,再异步落库
合并 SQL + 启用 JDBC 批处理参数
单条 INSERT 语句执行一次,网络往返、SQL 解析、日志刷盘各算一次开销;而一条含 100 组 VALUES 的 INSERT,只触发一次解析和一次 redo log 批量写入。
- MyBatis-Plus 使用 rewriteBatchedStatements=true(MySQL 驱动参数),让 JDBC 自动将多条 INSERT 合并为 multi-value 形式
- 配合 useServerPrepStmts=false(禁用服务端预编译),避免预编译语句在高并发下成为元数据锁瓶颈
- 手动拼接时注意单条 SQL 长度限制(
max_allowed_packet),建议单批不超过 1000 行,避免截断或报错
规避唯一索引引发的隐式锁竞争
当表存在唯一索引(如业务单号、联合唯一键)时,MySQL 在插入前需检查冲突,该过程会加 INSERT INTENTION LOCK 并与 gap lock 交互,是死锁高发区。
立即学习“Java免费学习笔记(深入)”;
- 插入前不做 SELECT 判断是否存在——改用 INSERT ... ON DUPLICATE KEY UPDATE 或 REPLACE INTO,由数据库原子处理
- 若必须生成唯一编号,优先使用数据库自增 ID、UUID 或雪花算法,而非依赖“查+算+插”三步逻辑
- 对高频插入字段,评估是否可放宽唯一性约束(如用应用层幂等校验替代数据库唯一索引)
调整索引与表结构降低锁粒度
非必要索引越多,插入时维护成本越高,且每个多余索引都可能成为锁竞争点。
- 删除未被查询使用的冗余索引,特别是组合索引中前导列不常用于 WHERE 条件的
- 对仅用于排序或分页的字段,考虑用覆盖索引替代全字段索引,减少聚簇索引更新压力
- 若表写远大于读,可临时关闭唯一索引(仅限离线导入场景),导入完成后再重建


















