避免undolog膨胀必须从应用层堵住长事务源头,配合数据库层实时监控与连接超时控制;仅调参innodb_max_purge_lag无效,需关联innodb_trx与processlist精准定位客户端,并严格管控事务生命周期、分页批量操作及只读事务提交。

直接上结论:避免 UndoLog 膨胀,不能只靠 innodb_max_purge_lag 调参,必须在应用层堵住长事务源头,并配合数据库层的实时监控与连接级超时控制。
查长事务不能只看 INNODB_TRX,得关联 PROCESSLIST 定位真实客户端
单独查 INNODB_TRX 只能看到事务状态,但无法判断它来自哪个服务、IP 或 SQL 上下文。不关联 PROCESSLIST,容易误杀或漏判。
-
trx_state = 'RUNNING'且trx_query IS NULL:大概率是应用开了事务但没提交,也可能是被阻塞卡在锁等待(需再查INNODB_LOCK_WAITS) -
trx_rows_modified > 0且duration_sec > 300:正在执行写操作的大事务,优先联系业务方确认是否可中断,而非直接 KILL - 用这条语句快速定位并溯源:
SELECT t.trx_id, t.trx_started, t.trx_state, t.trx_rows_modified, p.ID, p.USER, p.HOST, p.DB, p.COMMAND, p.TIME, p.INFO FROM information_schema.INNODB_TRX t JOIN information_schema.PROCESSLIST p ON t.trx_mysql_thread_id = p.ID WHERE TIME_TO_SEC(TIMEDIFF(NOW(), t.trx_started)) > 600;
MySQL 连接层超时设置要分两层:wait_timeout 和 interactive_timeout
很多应用连接池(如 HikariCP)配置了 connection-timeout,但 MySQL 服务端若未设 wait_timeout,空闲事务仍会一直挂着——因为连接池释放的是“逻辑连接”,MySQL 看到的仍是活跃会话。
-
wait_timeout控制非交互式连接(如应用连接池发起的连接)空闲超时,默认 28800 秒(8 小时),建议调为300(5 分钟) -
interactive_timeout控制交互式连接(如 mysql cli)空闲超时,可保留稍长(如 600),避免 DBA 手动操作被断开 - 注意:这两个参数对已执行 SQL 但未提交的事务无效;它们只作用于“无任何命令发送”的空闲连接
- 修改后需重启连接才生效,旧连接仍按原值计时
应用代码里必须显式控制事务生命周期,ORM 不等于自动兜底
MyBatis、Spring @Transactional、Django ORM 都不会替你决定“什么时候该 COMMIT”。一个 HTTP 请求里开启事务,中间调了外部 API 或 sleep 3 秒,就足以让 UndoLog 开始堆积。
- 禁止在事务内做 I/O:HTTP 调用、文件读写、消息队列 publish、日志刷盘等都必须移出事务块
- 批量更新必须分页:单次事务最多处理 1000~5000 行,每批后
COMMIT;用ROW_COUNT()或应用层计数校验,防漏 - 显式关闭事务:即使只读查询,也要避免
BEGIN后无COMMIT/ROLLBACK;Spring 中慎用@Transactional(propagation = Propagation.REQUIRED)套嵌过深 - MySQL 8.0+ 推荐启用
innodb_strict_mode = ON,防止INSERT ... SELECT这类语句隐式生成巨量 Undo 却不被感知
监控告警必须盯死两个硬指标:history list length 和长事务数量
磁盘空间涨到 90% 才报警,Undo 已经爆了。真正关键的是内存中不可见但持续膨胀的“历史版本压力”。
-
History list length > 5000:PURGE 明显滞后,需人工介入查最老事务;> 10000 且持续上涨,说明 purge 线程跟不上或被卡死 - 运行超 300 秒的事务数 > 3:触发企业微信/钉钉告警,附带
trx_id、thread_id、duration_sec、SUBSTRING(trx_query, 1, 100) - 别依赖单次查询:把检测脚本固化为每分钟执行一次的 cron + curl 告警,而不是靠人工
SHOW ENGINE INNODB STATUS\G - 注意:
History list length不会因 KILL 立刻下降,需等回滚完成 + PURGE 跟进,延迟几秒至几分钟不等
最常被忽略的一点:只读事务(trx_rows_modified = 0)同样会拖慢 Undo 清理——只要它开启了事务且未提交,就会持有 Read View,阻止 purge 清理其开始前的所有旧版本。所以“只读就安全”是错觉,事务边界必须和业务语义严格对齐。











