MySQL 8.0Notes

第 21 章:Purge 与清理

zjc 于 2026-01-21 发布

这是《MySQL 8.0 源码与内核实战》的独立章节版。本章从概念、实操和生产排查三个视角展开,代码块保留了原书可直接运行的版本。 DELETE 和 UPDATE 不一定立即物理删除旧记录。Purge 线程负责清理 delete mark、undo 历史和无效页,是空间回收和版本链缩短的关键。

21.1 为什么需要 Purge

UPDATE row
  -> old version in undo
     -> current record points to old version
        -> old snapshots may need it
           -> purge after no reader needs it

Purge 做的事:

  1. 从 undo history 找可清理记录;
  2. 物理删除 delete-mark 记录;
  3. 清理索引记录;
  4. 推进 purge lag;
  5. 尝试页合并;
  6. 回收 undo 空间。

21.2 可见性与清理边界

Purge 只能清理早于最老活跃 read view 的版本:

oldest active read view
  -> purge cannot remove newer needed versions

因此长事务、长查询、大事务和延迟提交都会阻碍 purge。

21.3 参数

参数 说明
innodb_purge_threads purge 线程数
innodb_purge_batch_size 批处理大小
innodb_max_purge_lag 延迟 DML 控制阈值
innodb_max_purge_lag_delay 最大延迟
innodb_undo_log_truncate_frequency undo truncate 频率

21.4 观测

SHOW GLOBAL STATUS LIKE 'Innodb_history_list_length';
SHOW ENGINE INNODB STATUS;
SELECT trx_id, trx_started
FROM information_schema.innodb_trx
ORDER BY trx_started;

排查:

现象 方向
history list 增长 长事务、purge 慢
undo 膨胀 purge 阻塞、空间压力
delete 后磁盘不降 页碎片、未合并
写入延迟 purge 滞后、二级索引
CPU 高 purge 并发过高

21.5 大删除

不推荐一次性删除超大范围:

DELETE FROM logs WHERE created_at < '2025-01-01';

推荐分批:

DELETE FROM logs
WHERE created_at < '2025-01-01'
ORDER BY id
LIMIT 1000;

每批之间提交,让 purge 和复制有机会推进。历史表可使用分区后 DROP PARTITION

21.6 源码入口

文件 职责
trx0purge.cc purge 主流程
row0purge.cc 行级清理
row0umod.cc undo 修改处理
row0uins.cc 插入 undo

断点:

b trx_purge
b row_purge_record

本章小结

Purge 清理旧版本和 delete-mark 记录,推进依赖最老活跃快照。长事务会让 history list 和 undo 空间持续增长;大批删除应分批或使用分区。

思考题

  1. 为什么 DELETE 后磁盘空间可能不变?
  2. history list length 反映什么?
  3. 长查询如何影响 purge?
  4. 大范围删除如何分批?
  5. purge 和 undo truncate 有什么关系?