MVCC 与 purge 滞后是什么
InnoDB 用多版本并发控制:修改数据时把旧版本写进 undo,让读事务能看到自己快照时刻的数据。代价是旧版本必须等到没有事务再需要它们才能清理,这个动作叫 purge。一旦有长事务或长时间未提交的只读事务挂着,purge 就追不上,history list 持续增长、undo 表空间膨胀、磁盘告警接连出现,查询也会因为要遍历更长的版本链而变慢。
基本信息
| 适用版本 | MySQL 5.7 / 8.0(8.0 的 undo 表空间默认独立文件) |
|---|---|
| 核心指标 | History list length(SHOW ENGINE INNODB STATUS 的 TRANSACTIONS 段) |
| 相关视图 | information_schema.INNODB_TRX、INNODB_TABLESPACES |
| 常见诱因 | 长事务、长时间未提交的只读事务、大批量单事务加载 |
| 症状 | undo 表空间增长、磁盘告警、查询逐渐变慢 |
| 治理方向 | 找出并结束老事务,把大事务拆成小批次提交 |
典型使用场景
- 磁盘突然告警排查:确认增长来自 undo 表空间而非业务数据
- 查询莫名变慢:版本链变长导致回滚段读取成本上升
- 定时任务排查:批处理单事务跑几十分钟,把 purge 拖住
- 报表与导出场景:长只读事务同样阻止旧版本回收
- 监控建设:把 history list 长度纳入日常指标而不是等磁盘报警
观测命令
观测命令
-- 历史版本链表长度:持续增长即为 purge 滞后
SHOW ENGINE INNODB STATUS\G # TRANSACTIONS 段的 History list length
-- 未提交事务清单,按开始时间排序找出最老的
SELECT trx_id, trx_state, trx_started, trx_rows_locked, LEFT(trx_query, 60)
FROM information_schema.INNODB_TRX ORDER BY trx_started;
-- undo 表空间占用
SELECT NAME, FILE_SIZE FROM information_schema.INNODB_TABLESPACES
WHERE NAME LIKE 'innodb_undo%';
-- 是否有长事务对应的连接
SELECT id, user, host, time, state, LEFT(info, 60) FROM information_schema.PROCESSLIST
WHERE time > 300 ORDER BY time DESC;治理动作
治理动作
-- 1) 先定位最老事务对应的会话,再评估影响面
SELECT trx_mysql_thread_id, trx_started FROM information_schema.INNODB_TRX ORDER BY trx_started LIMIT 5;
-- 2) 确认是失控事务后再终止(大事务回滚本身会消耗 IO)
KILL 12345;
-- 3) 批处理改为小批次提交,控制单事务耗时
-- DELETE FROM big_table WHERE created_at < '2025-01-01' LIMIT 2000; -- 循环执行
-- 4) 只读长任务放到从库或使用可关闭快照的方式执行指标对照
| 指标 | 正常范围 | 异常含义 |
|---|---|---|
| History list length | 数百以内(视写入量) | 持续增长说明有事务阻止清理 |
| INNODB_TRX 最老事务时长 | 秒级到分钟级 | 小时级说明有长事务悬挂 |
| undo 表空间占用 | 稳定或小幅波动 | 持续上涨伴随磁盘告警 |
| trx_rows_locked | 与业务行数匹配 | 异常大表示锁范围过宽或条件未走索引 |
只读事务同样拖住 purge:REPEATABLE READ 下开始一个只读事务后不提交,快照会一直有效,旧版本无法回收;报表、导出类任务应放到从库执行,或使用能在每次查询后释放快照的方式。
发现 undo 膨胀就扩容磁盘只是续命:根源是长事务不除,purge 依旧追不上,最终会同时遇到磁盘写满与查询性能下降两个问题;应当先降低单事务时长,再评估 undo 表空间回收方式。