事务包裹批量insert可省去90%的重复开销:仅1次sql解析、1次锁获取与释放、1次redo log刷盘、1次binlog写入,大幅减少网络往返和cpu开销。

事务包裹批量INSERT能省掉哪些开销?
不加事务时,每条INSERT语句默认自动提交,意味着每次都要走完整流程:网络往返、SQL解析、加锁、写redo log、刷binlog、释放锁、返回确认。1000条数据就是1000次这些动作。用beginTransaction() + commit()包裹后,这些开销被大幅压缩——SQL只解析一次、锁竞争窗口缩短、redo log批量刷盘一次、binlog也只写一次事件(如果用STATEMENT格式)或一个事务组(ROW格式)。
为什么不用事务反而容易卡住数据库?
自动提交模式下,每条INSERT都独立成事务,锁(尤其是二级索引上的间隙锁)会频繁获取又释放,极易引发锁等待甚至死锁;同时大量小事务导致redo log频繁刷盘,I/O压力陡增。而显式事务把锁持有时间集中在一个窗口内,虽然单次事务更长,但总锁竞争次数下降,整体并发吞吐反而上升。另外,没包裹事务时,程序异常中断会导致已执行的语句无法回滚,数据状态不可控。
autocommit = 0和beginTransaction()有什么区别?
二者效果相似,但行为细节不同:
-
SET autocommit = 0是会话级开关,之后所有INSERT都进入隐式事务,直到显式COMMIT或ROLLBACK;但容易遗漏提交,导致连接长期持锁、阻塞其他操作 -
beginTransaction()是显式声明事务起点,语义清晰,配合try/catch可确保commit()或rollback()必选其一,更安全 - 注意:
START TRANSACTION语句本身会隐式关闭当前事务(如果存在),而autocommit = 0不会自动结束已有事务
批次大小和事务边界怎么配?
事务不是越大越好。单个事务包含5000行数据,可能触发max_allowed_packet限制(尤其拼接VALUES时),也可能让undo log暴涨、主从延迟加剧。实操中建议:
- 每批控制在
100–500行,对应单条INSERT ... VALUES (),(),...语句长度约100KB–1MB - 用外层循环分批,每批用独立
beginTransaction()→ 执行 →commit(),避免单事务过大 - 不要把10万行塞进一个事务里——你以为省了开销,其实是在攒IO压力和锁风险
真正容易被忽略的是:事务边界必须与业务语义对齐。比如插入订单+订单明细,这两张表的操作必须包在同一个事务里;但不同用户的批量导入,完全可以拆成多个小事务并行跑——别为了“减少事务数”而强行合并无关操作。











