innodb_max_purge_lag只是节流阀而非清洁工,仅在未purge undo超限时延迟写入以缓解压力,无法清理已堆积的history list或解决长事务阻塞问题。

单纯调大 innodb_max_purge_lag 不会解决 Undo Log 膨胀,它只在空间快耗尽时“踩刹车”,不清理已存在的堆积。
为什么 innodb_max_purge_lag 不能清掉历史 undo
这个参数本质是“节流阀”,不是“清洁工”。当未 purge 的 undo 记录数超过设定值(比如 500000),InnoDB 就会在每个 INSERT/UPDATE/DELETE 前插入微秒级延迟(由 innodb_max_purge_lag_delay 控制),从而降低新写入速度,给 purge 线程腾出资源。但它对已经卡住的长事务、早已堆积的 history list length 毫无作用。
常见误判场景:
- 把
innodb_max_purge_lag = 0当成“关闭限流”——实际等于禁用保护,undo 持续膨胀直到磁盘满 - 把
innodb_max_purge_lag设得极小(如10000)——导致正常批量导入也频繁被 throttle,TPS 断崖下跌 - 调完参数就以为问题解决,却没查
INNODB_TRX表里那个运行了 8 小时、trx_query IS NULL的空闲事务
怎么设才不算瞎调:推荐组合与观察点
初始建议值不是拍脑袋来的,而是兼顾 purge 能力与业务写入容忍度:
-
innodb_max_purge_lag = 500000(约 50 万条未 purge undo 记录) -
innodb_max_purge_lag_delay = 100000(100 微秒延迟,足够缓压又不至于卡死) - 必须配合监控
SHOW ENGINE INNODB STATUS输出里的PURGE DONE进度,看它是否在缓慢推进 - 如果
History list length长期 > 50000 且不上不下,说明 purge 线程本身被拖慢(比如磁盘 IO 已饱和或线程数不足)
真正起效的前提:先干掉阻塞 purge 的长事务
再好的 innodb_max_purge_lag 配置,遇上一个 trx_state = 'RUNNING' 且 trx_query IS NULL 的事务,就彻底失效。这类事务不释放 ReadView,所有旧版本 undo 全被钉死。
必须做的三件事:
- 用这个语句捞出真凶:
SELECT trx_id, trx_started, TIME_TO_SEC(TIMEDIFF(NOW(), trx_started)) AS duration_sec, trx_state, trx_query FROM information_schema.INNODB_TRX WHERE TIME_TO_SEC(TIMEDIFF(NOW(), trx_started)) > 600 - 区分对待:
trx_state = 'RUNNING'+trx_query IS NULL→ 可安全 KILL;trx_state = 'ROLLING BACK'→ 别动,等它自己结束 - 查
performance_schema.threads关联trx_mysql_thread_id,定位到具体应用 IP 和用户,推动业务侧修复连接池未 close 或事务未 commit 的逻辑
别忘了 undo 表空间本身的回收能力
innodb_max_purge_lag 管的是“purge 速度”,但 undo 文件物理大小能否收缩,取决于另一套机制:
- 必须开启
innodb_undo_log_truncate = ON -
innodb_undo_tablespaces至少为 3(默认是 2,不够轮换) - 执行
ALTER UNDO TABLESPACE innodb_undo_002 SET INACTIVE后,purge 线程才会尝试截断该文件 - 注意:截断只发生在系统空闲、且无长事务持有该 undo 表空间中任何回滚段时
也就是说,即使 purge 完成了,undo_002.ibu 文件也不会自动变小——除非你主动把它设为 inactive 并等待后台完成 truncate。











