短事务是MySQL默认推荐,因长事务持续占用锁、undo log和连接资源,导致锁竞争、主从延迟、purge阻塞等问题;其核心在于InnoDB需全程维护行锁、undo记录及read view,资源开销直接反映在INNODB_TRX表指标中。

短事务为什么是MySQL的默认推荐
因为长事务会持续占用锁、undo log 和连接资源,而这些资源在高并发下极其稀缺。MySQL 的事务不是“开个头就完事”,从 BEGIN 到 COMMIT 或 ROLLBACK 的整个区间,InnoDB 都在后台维持状态——这直接拖慢其他事务、抬高主从延迟、甚至触发 purge 线程阻塞。
事务启动后到底占了哪些资源
事务一旦开启,InnoDB 就开始维护三类关键状态:
- 行锁/间隙锁不释放:只要事务没结束,它持有的所有锁(哪怕只更新一行)都继续生效,其他事务想改同一批数据就得排队等待
- undo log 持续增长:每条修改都会生成 undo 记录,用于回滚和 MVCC 可见性判断;长事务让旧版本数据无法被 purge 清理,undo 表空间可能快速膨胀
- 活跃事务视图(read view)长期存在:RR 隔离级别下,事务启动时创建的 read view 会一直保留到提交,导致其他事务的 delete/update 产生的新版本无法被回收
这些不是“理论开销”,而是能直接在 information_schema.INNODB_TRX 里看到的实时指标:TRX_ROWS_LOCKED、TRX_UNDO_LOG_ENTRIES、TRX_STARTED 时间戳全都在告诉你这个事务“吃”了多少。
常见长事务陷阱与对应表现
很多看似合理的写法,实际悄悄打开了长事务黑洞:
- 在事务里调用
curl_exec()或file_get_contents()等外部 HTTP 请求——网络延迟秒级起步,锁就挂起秒级 - 显式开启事务后,用
SELECT ... FOR UPDATE锁住一批数据,再在应用层循环处理每条记录——锁持有时间 = 处理 N 条记录的总耗时 - 异常未捕获导致
COMMIT/ROLLBACK被跳过,连接空置在事务中(尤其 ORM 自动 begin 后忘记手动收尾) - 批量任务用单个事务更新 10 万行:
UPDATE t SET status=1 WHERE id BETWEEN 1 AND 100000——不仅锁多,还极易触发自增锁争用或死锁
这类操作在日志里往往表现为 Lock wait timeout exceeded 或主从延迟飙升,但根因不在 SQL 本身,而在事务生命周期失控。
如何验证和约束事务长度
不能只靠“自觉”,得用机制卡住上限:
- 在应用层设置事务超时:比如 PDO 设置
PDO::ATTR_TIMEOUT,或框架里配置transaction_timeout=5(单位秒) - 数据库侧主动监控:定期查
SELECT * FROM information_schema.INNODB_TRX WHERE TIME_TO_SEC(TIMEDIFF(NOW(), TRX_STARTED)) > 5,对超时事务告警甚至 kill - 避免隐式事务陷阱:确认
autocommit=1,防止某条UPDATE被意外包进未关闭的事务上下文 - 批量更新必须分片:用
WHERE id BETWEEN ? AND ?拆成 1000 行/批,每批独立事务,配合innodb_lock_wait_timeout控制单次等待上限
真正难的不是“知道要短”,而是把事务边界从代码逻辑里明确切出来——比如“校验库存”和“扣减库存”是否属于同一事务,取决于你能否接受中间状态被其他请求看到。这点一旦模糊,锁和性能问题就注定出现。











