长事务会持续占用锁、阻塞DDL、堆积undo日志并拉长MVCC版本链,导致ALTER TABLE卡住、从库延迟突增、SELECT ... FOR UPDATE变慢等;需通过information_schema.innodb_trx与performance_schema定位并分类处置。

长事务会直接拖慢整个 MySQL 实例
不是“可能影响”,而是只要存在活跃的长事务,就会持续占用锁、阻塞 DDL、堆积 undo 日志,并让 MVCC 版本链不断变长。最直观的表现是 show processlist 里出现大量 State: Sending data 或 Waiting for table metadata lock,同时 innodb_trx 表中 trx_started 时间远早于当前时间。
常见错误现象包括:ALTER TABLE 卡住、从库延迟突增、SELECT ... FOR UPDATE 响应变慢、甚至触发 Lock wait timeout exceeded 错误。
- 长事务不等于慢查询——一个只读
SELECT执行 5 分钟,只要没提交,就算长事务 - 事务开启后未显式
COMMIT或ROLLBACK(比如应用异常退出、连接池未正确 close)是最常见原因 - MySQL 8.0+ 中
information_schema.innodb_trx的trx_started和trx_state是第一排查依据
用 innodb_trx + performance_schema 快速定位长事务
别只看 show processlist,它只能看到线程状态,看不到事务实际生命周期。必须查 information_schema.innodb_trx,再关联 performance_schema.threads 和 events_statements_current 找出原始 SQL。
实操建议:
- 执行
SELECT trx_id, trx_started, trx_state, trx_mysql_thread_id FROM information_schema.innodb_trx WHERE TIME_TO_SEC(TIMEDIFF(NOW(), trx_started)) > 60;查超 1 分钟的事务 - 用
SELECT * FROM performance_schema.events_statements_current WHERE thread_id = ?(? 替换为上一步的trx_mysql_thread_id)获取最后执行语句 - 注意:若
events_statements_current为空,说明该线程当前没在执行语句,极可能是应用端开了事务但卡在业务逻辑里没提交
避免长事务的三个硬性约束点
优化不在事后杀进程,而在事前卡住风险入口。重点盯死这三处:
- 应用层:所有数据库操作必须包裹在明确的
BEGIN/COMMIT/ROLLBACK块中,禁止靠连接关闭自动提交(autocommit=1时单条语句是事务,但显式BEGIN后就不是) - 框架层:Spring 的
@Transactional默认传播行为是REQUIRED,若嵌套调用且未配置timeout,外部事务可能被内部耗时操作拖长;务必设置timeout = 30(单位秒) - 运维层:在 MySQL 配置中加
wait_timeout = 60和interactive_timeout = 60,让空闲连接自动断开,防止“挂着事务却不干活”的僵尸连接
已存在的长事务不能直接 kill,要分场景处理
KILL 一个长事务看似快,但后果不可控:若它正在写大表、回滚会产生巨量 I/O,甚至导致实例卡死。必须先判断类型再行动。
- 只读事务(
trx_isolation_level = 'READ COMMITTED'且无FOR UPDATE):可安全KILL,不影响数据一致性 - 写事务但已执行完修改、卡在应用层未提交:优先联系对应服务负责人确认是否能手动提交;若无法联系,再考虑
KILL - 写事务且正在执行中(
trx_state = 'RUNNING'):不要KILL,等它自然结束或增大innodb_rollback_on_timeout观察回滚进度
真正难处理的,是那种开着事务、连着连接、但应用完全不响应也不释放的“幽灵事务”——它不会自己消失,也不会报错,只能靠 wait_timeout 机制被动清理。这种往往暴露的是应用连接管理缺陷,不是 DBA 能单点修复的。



















