长事务卡住最老read view导致undo log无法清理,必须先终止trx_state='running'且trx_query is null的事务;否则purge线程被钉死,history list length持续升高,调参无效。

长事务卡住最老的 read view,undo 就不敢删
Undo Log 不是“用完就扔”,InnoDB 必须确保所有可能需要读取旧版本的事务都已结束,才能安全清理对应 undo 记录。而长事务会持续持有最老的 read view,导致 purge 线程无法清理它之后产生的所有历史版本——哪怕这些事务早已提交。
典型表现是 SHOW ENGINE INNODB STATUS\G 中的 HISTORY LIST LENGTH 持续 > 5000 甚至上万,且不下降。这不是 purge 线程懒,是它被“钉死”在某个时间点动弹不得。
- 只要有一个
trx_state = 'RUNNING'且trx_query IS NULL的事务(比如应用崩溃后连接没关),它创建的read view就一直有效 -
REPEATABLE READ隔离级别会让这个read view生命周期更长,比READ COMMITTED更容易拖住 purge - 只读事务(
trx_rows_modified = 0)同样会阻塞 purge,只是危害小些
杀错线程反而让 purge 更慢
盲目 KILL 可能让问题恶化:正在回滚的事务(trx_state = 'ROLLING BACK')一旦被 KILL,InnoDB 会中断当前回滚流程,转为更耗 IO 的后台异步清理,反而加剧磁盘压力和 purge 延迟;而处于锁等待(trx_state = 'LOCK WAIT')的事务,KILL 它之前必须先找到并干掉上游持锁者,否则只是换一个等锁的线程。
- 优先查
INNODB_LOCK_WAITS找blocking_trx_id,再 kill 上游 -
trx_state = 'RUNNING'且trx_query IS NULL的才适合立即 kill - KILL 前用
SELECT * FROM information_schema.PROCESSLIST WHERE ID = ?确认HOST和USER,避免误杀监控或备份连接
innodb_max_purge_lag 是限流器,不是清道夫
设 innodb_max_purge_lag = 0 不会让 undo 立刻消失,它只是让每个新 DML 插入前强制休眠,把写入速度压到极低——但堆积的 undo 还在,purge 线程若仍被长事务卡住,HISTORY LIST LENGTH 依然不降,业务还全卡住。
- 这个参数对已存在的长事务、已堆积的 undo log 完全无效
- 它不触发 purge,只让新写入“让路”;真正决定 purge 能否跟上的,是
innodb_purge_threads、磁盘随机 IO 能力、以及有没有事务钉住最老read view - MySQL 8.0 默认值为 0,不代表安全,而是默认期望你靠监控+应用治理兜底
缩容 undo 表空间前必须确认状态为 INACTIVE
即使 purge 跟上了、HISTORY LIST LENGTH 已回落,也不能直接 DROP UNDO TABLESPACE。必须先执行 ALTER UNDO TABLESPACE undo_001 INACTIVE,再查 information_schema.FILES 确认其 STATE 变为 INACTIVE,且无任何活跃事务依赖该空间。
- 在
ACTIVE状态下 DROP 会导致实例崩溃 - 直接删物理文件(如
undo_001)同样危险,InnoDB 不会感知,后续可能报错或静默损坏 - 缩容是最后一步,不是第一步;没清理干净就缩容,等于把未释放的 undo “封印”进磁盘,下次启动仍要加载
真正卡住 undo 回收的,从来不是配置参数,而是那个没人管、没日志、trx_query 为空却活了两小时的连接。它不报错,不阻塞查询,只默默钉死 purge 线程,直到磁盘告警响起。











