必须先查innodb_trx定位trx_state='running'且trx_query is null的空闲未提交事务,再分状态kill、加大purge_batch_size、启用undo自动截断并执行truncate;仅调参无效。

怎么快速定位真正卡住 purge 的长事务
别信 SHOW PROCESSLIST 里的 Time 字段——它只统计当前语句执行时长,和 undo 持有无关。真正钉死 purge 的,是那些 trx_state = 'RUNNING' 且 trx_query IS NULL 的空闲事务,它们早执行完了,但没提交,导致历史版本全被锁住。
运行这条语句查最老的活跃事务:
SELECT trx_id, trx_started, trx_state, trx_rows_modified, trx_mysql_thread_id FROM information_schema.INNODB_TRX ORDER BY trx_started LIMIT 5;
- 重点关注
trx_started超过 600 秒(10 分钟)、trx_state = 'RUNNING'、trx_query IS NULL的记录 - 用
trx_mysql_thread_id关联information_schema.PROCESSLIST,确认HOST、USER、COMMAND和INFO,判断是不是 Python 进程崩溃后连接未关闭,或是 ORM 开了事务却忘了COMMIT -
trx_rows_modified = 0的只读事务也会阻塞 purge,但危害小;trx_rows_modified > 10000的写事务才是高危对象
kill 前必须分状态处理,否则可能更糟
盲目 KILL 可能让实例雪上加霜。不同 trx_state 必须区别对待:
-
trx_state = 'RUNNING'且trx_query IS NULL:99% 是应用泄漏,可安全KILL对应thread_id -
trx_state = 'LOCK WAIT':它被别的事务堵住了,先查INNODB_LOCK_WAITS找出blocking_trx_id,优先干掉上游 -
trx_state = 'ROLLING BACK':别动!此时KILL会让回滚从同步变异步,IO 更爆、purge 更慢,甚至拖垮整个清理线程 - 若
trx_rows_modified > 100000,回滚可能持续数分钟,KILL前务必评估业务影响和恢复窗口
杀完源头后,如何真正释放 undo 空间
杀掉长事务后,History list length 不会立刻下降,磁盘空间也不会自动回收。物理文件不收缩是正常现象,关键在推动 purge 清理并启用截断机制:
- 先确认已启用独立 undo 表空间:
SHOW VARIABLES LIKE 'innodb_undo_tablespaces';值必须 ≥ 2(推荐设为 4) - 临时加大清理力度:
SET GLOBAL innodb_purge_batch_size = 10000;(默认 300) - 开启自动截断:
SET GLOBAL innodb_undo_log_truncate = ON; - 对每个 undo 表空间执行:
ALTER UNDO TABLESPACE undo_001 TRUNCATE;—— 但前提是History list length已降到 ≤ 100,否则会失败 - 若仍用共享表空间(
innodb_undo_tablespaces = 0),无法在线收缩,只能mysqldump全库重建
为什么调 innodb_max_purge_lag 没用
这个参数本质是节流阀,不是清洁工。它只在未 purge 的 undo 记录数超限时,给新写入加微秒级延迟,从而缓解压力,但对已堆积的 History list length 完全无效。
- 设
innodb_max_purge_lag = 0不是“关闭限流”,而是禁用保护,undo 会持续膨胀直到磁盘满 - 设得过小(如 10000)会导致正常批量导入也被频繁 throttle,TPS 断崖下跌
- 所有调参都建立在一个前提上:最老的
trx_state = 'RUNNING'且trx_query IS NULL的事务已被终止 - 检查是否生效,要看
SHOW ENGINE INNODB STATUS\G中 “PURGE DONE for trx's n:o











