长事务会拖住二级索引的Purge清理,因其持有的Read View阻止undo记录被安全删除,且二级索引更新产生更多undo日志;同时导致MVCC可见性检查变慢,表现为二级索引查询性能下降。

长事务如何拖住二级索引的Purge清理
二级索引(Secondary Index)的Purge延迟不是独立发生的,而是被长事务“连带卡死”的结果。InnoDB 的 purge 机制不区分主键和二级索引——只要一个事务还活跃,它创建的 Read View 就会把所有它开始之后生成的旧版本行(包括聚簇索引记录 + 对应的二级索引条目)都标记为“可能可见”,purge 线程就无法安全删除这些 undo 记录。而二级索引更新比主键更“费 undo”:一次 UPDATE 涉及二级索引列时,InnoDB 会为每条二级索引记录单独写一条 undo log,导致 undo 量翻倍甚至更多。
为什么二级索引查询变慢常被误判为“索引失效”
现象是 SELECT ... FROM t WHERE idx_col = ? 明明走了二级索引,执行时间却越来越长,EXPLAIN 显示 key 正确、rows 却异常高。这不是索引设计或统计信息问题,而是版本链膨胀的副作用:每次通过二级索引定位到叶子页后,InnoDB 还要回表(或遍历二级索引自身的版本链)做可见性判断,而长事务导致每个二级索引项背后挂了几十甚至上百个旧版本,遍历成本线性上升。你看到的“慢”,本质是 MVCC 可见性检查在二级索引路径上反复跳转undo链造成的。
INNODB_TRX里哪些字段暴露二级索引Purge受阻
单看 trx_rows_modified 不够——它只统计聚簇索引修改行数。真正提示二级索引压力大的信号藏在关联分析中:
- 查出
trx_state = 'RUNNING'且trx_query IS NULL的事务后,用其trx_mysql_thread_id关联information_schema.PROCESSLIST,重点看INFO字段是否含UPDATE/DELETE且 WHERE 条件命中二级索引(比如WHERE status=1) - 执行
SHOW ENGINE INNODB STATUS\G,搜索HISTORY LIST LENGTH;若该值 > 5000,再看PURGE DONE for trx's n:o是否长期停滞,说明 purge 已卡在包含大量二级索引 undo 的事务边界上 - 监控
INFORMATION_SCHEMA.INNODB_METRICS中的innodb_purge_undo_removal_rate,若持续低于innodb_purge_trx_removal_rate,说明 undo 清理(尤其是二级索引产生的 undo)严重滞后
调参前必须先杀掉的那类事务
别碰 innodb_purge_batch_size 或 innodb_purge_threads,除非你已确认并清除了以下两类事务:
-
trx_state = 'RUNNING'且trx_query IS NULL:99% 是应用层漏了COMMIT或连接池未 close,它们空占着 trx_id 却不干活,直接KILL -
trx_rows_modified > 5000且duration_sec > 120:大概率正在批量更新带二级索引的字段(如日志表UPDATE log SET status=2 WHERE create_time < '2026-09-01'),这种事务必须人工介入,不能等它自己结束
History list length 不下降,调任何 purge 参数都是白忙——因为 purge 线程根本没机会启动真正的清理循环,它一直堵在最老 Read View 那一关。



















