undo log暴涨本质是长事务卡住purge线程导致历史版本无法清理,必须先终止trx_state='running'且trx_query is null的最老事务;定位需查innodb_trx按trx_started排序并结合processlist确认异常连接;处理须分状态kill,清理后加大innodb_purge_batch_size并启用undo截断,根治须从应用层杜绝事务泄漏。

Undo Log暴涨不是磁盘空间配置问题,是长事务卡住 purge 线程导致历史版本无法清理。不先终止最老的活跃事务,调任何参数都无效。
怎么快速定位真正卡住 purge 的长事务
SHOW PROCESSLIST 的 Time 字段不可信——它只反映当前语句执行时长,而真正钉死 undo 的,是那些早已空闲却没提交的事务。
必须查 information_schema.INNODB_TRX:
SELECT trx_id, trx_started, TIME_TO_SEC(TIMEDIFF(NOW(), trx_started)) AS duration_sec, trx_state, trx_rows_modified, trx_query FROM information_schema.INNODB_TRX WHERE TIME_TO_SEC(TIMEDIFF(NOW(), trx_started)) > 600;
重点关注: -trx_state = 'RUNNING' 且 trx_query IS NULL:极大概率是应用漏了 COMMIT 或连接池未关闭
- trx_rows_modified > 10000 且 duration_sec > 300:大写事务已卡住,需立刻干预
- 用 trx_mysql_thread_id 关联 information_schema.PROCESSLIST,确认来源 IP、用户和实际命令
KILL 前必须分状态判断,否则可能更糟
盲目KILL 会加剧 IO 压力、拖垮 purge,甚至让实例雪崩:
- trx_state = 'RUNNING' 且 trx_query IS NULL:可安全 KILL,99% 是连接泄漏或 ORM 事务未关闭
- trx_state = 'LOCK WAIT':先查 INNODB_LOCK_WAITS 找出 blocking_trx_id,优先干掉上游事务
- trx_state = 'ROLLING BACK':别动,此时 KILL 会让回滚从同步变异步,耗时翻倍、IO 更爆
- 若 SHOW ENGINE INNODB STATUS\G 中 History list length > 10000,批量 KILL 必须加 SLEEP(0.2) 间隔执行
杀完事务后,怎么让 purge 跟上并触发 undo 截断
杀掉源头后,History list length 不会立刻下降,需主动助推:
- 检查 PURGE DONE for trx's n:o 是否在推进(<code>SHOW ENGINE INNODB STATUS\G)
- 临时加大清理能力:SET GLOBAL innodb_purge_batch_size = 10000(默认 300)
- 确保已启用独立 undo 表空间:SHOW VARIABLES LIKE 'innodb_undo_tablespaces',值必须 ≥ 4(推荐)
- 开启自动截断:SET GLOBAL innodb_undo_log_truncate = ON
- 再执行:ALTER UNDO TABLESPACE undo_001 SET INACTIVE → 等 History list length 显著回落 → ALTER UNDO TABLESPACE undo_001 TRUNCATE为什么单独调 innodb_max_purge_lag 一定失败
这个参数本质是“节流阀”,不是“清洁工”:
- 它只在未 purge 的 undo 记录数超限时,对新 DML 插入微秒级延迟(由 innodb_max_purge_lag_delay 控制)
- 对已存在的长事务、已堆积的 History list length 完全无影响
- 设为 0 等于关闭保护,undo 持续膨胀直到磁盘满;设得太小(如 10000)会导致正常批量写入也被频繁 throttle
- 它生效的前提是:长事务已结束 + innodb_undo_tablespaces ≥ 2 + innodb_undo_log_truncate = ON最易被忽略的一点:所有 TRUNCATE 操作都依赖 purge 进度,而 purge 进度永远被最老的活跃事务控制。哪怕只漏掉一个 trx_state = 'RUNNING' 且 trx_query IS NULL 的连接,整个收缩流程就会卡死在 SET INACTIVE 或 TRUNCATE 阶段。











