必须先杀掉 trx_state = 'running' 且 trx_query is null 的事务,否则调参数、开截断、加purge批次均无效;undo膨胀是purge线程卡死信号,需用innodb_trx查trx_started超600秒、state为running且query为空的事务,结合processlist确认身份后精准kill,并验证purge done进度是否推进。

必须先杀掉 trx_state = 'RUNNING' 且 trx_query IS NULL 的事务,否则调任何参数、开截断、加 purge 批次都无效——undo log 膨胀不是磁盘空间问题,是 purge 线程被卡死的明确信号。
怎么用 INNODB_TRX 快速揪出真凶
别信 SHOW PROCESSLIST 的 Time 字段,它只统计当前语句执行时长,和事务开了多久完全无关。真正钉住 undo 的,是那些早已空闲但没提交的连接。
- 运行这条语句定位最老的几个可疑事务:
SELECT trx_id, trx_started, TIME_TO_SEC(TIMEDIFF(NOW(), trx_started)) AS duration_sec, trx_state, trx_rows_modified, trx_mysql_thread_id FROM information_schema.INNODB_TRX WHERE TIME_TO_SEC(TIMEDIFF(NOW(), trx_started)) > 600 ORDER BY trx_started LIMIT 5;
- 重点关注
duration_sec > 600、trx_state = 'RUNNING'、trx_query IS NULL的记录——99% 是应用崩溃后连接没 close,或 ORM 开了事务但忘了COMMIT -
trx_rows_modified > 10000的写事务也要盯紧,哪怕duration_sec只有 300 秒,回滚可能已卡住 - 用
trx_mysql_thread_id去关联information_schema.PROCESSLIST,确认HOST、USER、INFO,避免误杀定时任务或导出作业
KILL 前必须分状态处理,否则可能更糟
盲目 KILL 不是清理动作,而是高风险操作。不同 trx_state 对应完全不同的底层行为,乱杀会让 purge 更慢、IO 更爆,甚至拖垮实例。
-
trx_state = 'RUNNING'且trx_query IS NULL:可安全KILL对应线程,大概率是连接泄漏 -
trx_state = 'LOCK WAIT':它正被别的事务堵着,先查INNODB_LOCK_WAITS找出blocking_trx_id,优先干掉上游 -
trx_state = 'ROLLING BACK':绝对不要KILL,此时 InnoDB 正在同步回滚,KILL会强制转为异步,耗时翻倍、IO 暴涨 - 若
trx_rows_modified > 100000,回滚可能持续数分钟,KILL前务必确认业务是否允许中断
杀完之后怎么让 History list length 真正回落
杀掉事务不等于 undo 空间立刻回收。如果 History list length 不下降,说明 purge 线程根本没推进——这时候光调参数没用,得先确认它是不是被卡死了。
- 执行
SHOW ENGINE INNODB STATUS\G,搜PURGE DONE for trx's n:o行,看数字是否在往前走;再比对刚 kill 掉的trx_id是否已被覆盖 - 临时加大清理能力:
SET GLOBAL innodb_purge_batch_size = 10000(默认是 300) - 确认已启用独立 undo 表空间:
SHOW VARIABLES LIKE 'innodb_undo_tablespaces',值必须 ≥ 4(仅 ≥ 2 不够轮换) - 开启自动截断:
SET GLOBAL innodb_undo_log_truncate = ON;再对旧表空间执行ALTER UNDO TABLESPACE undo_001 SET INACTIVE→ 等History list length显著回落 →ALTER UNDO TABLESPACE undo_001 TRUNCATE
最容易被忽略的是:purge 进度必须肉眼验证,不能靠“我 kill 了就完事”。很多同学杀完就去改配置,结果 PURGE DONE 数字一动不动,history list 卡在 12000+,所有后续操作都在原地打转。真正的释放,永远始于确认 purge 在动。











