高并发delete压垮purge线程,因其生成大量undo记录而长事务阻塞清理,导致history list length飙升、select变慢、写入卡顿及备份失败;应合理设innodb_purge_threads为2~4并同步调大innodb_purge_batch_size至500~1000。

为什么高并发 DELETE 会让 purge 线程扛不住
不是 purge 线程“太慢”,而是它被大量待清理的 undo 记录压垮了。每次 DELETE(或 UPDATE)在 RR 隔离级别下,都会生成旧版本记录并留在 undo log 中;只要还有活跃事务依赖这些版本,purge 就不能删——而高并发写入 + 长事务(哪怕只是空闲连接)会让 HISTORY LIST LENGTH 持续飙升,最终拖慢 SELECT、卡住写入、甚至让备份失败。
innodb_purge_threads 调多少才合理
盲目设成 8 或 16 反而更糟:线程争抢 purge_sys->mutex,挤占 buffer pool 刷脏页资源,实际 purge 效率可能不升反降。
- 纯读或低频更新库:
innodb_purge_threads = 1就够用 - 高频
DELETE/UPDATE场景(如日志表清理、状态流转):设为2~4,别轻易超4 - 必须同步调大
innodb_purge_batch_size:从默认300提到500~1000,但别超2000 -
SET GLOBAL innodb_purge_threads = 4不会立刻生效——只影响下一次 purge 循环启动的新线程,旧线程会跑完当前 batch 才退出
分批 DELETE 必须满足的三个硬条件
光加 LIMIT 没用,漏删、重复删、全表扫锁表全可能发生。
- WHERE 条件必须命中索引最左前缀,推荐组合索引如
(status, id),确保每次都能快速定位起始位置 - 必须带
ORDER BY id LIMIT 1000:InnoDB 不保证无序扫描的物理顺序,不加ORDER BY会导致同一批反复删同一组行 - 每次删除后要锚定下一个起点,例如上一批最大
id是12345,下一批就用WHERE status = 0 AND id > 12345 ORDER BY id LIMIT 1000
删完数据,磁盘空间为什么还不释放
这是最常被忽略的一点:DELETE 只是标记删除,真正释放空间靠 purge 线程,而 purge 又受 HISTORY LIST LENGTH 和 undo 表空间配置制约。即使 purge 跑完了,.ibd 文件大小也不会自动缩小——InnoDB 不会把空出来的页还给文件系统。
- 想真正回收磁盘空间,必须执行
OPTIMIZE TABLE(会重建表,锁表时间长) - 或者改用
TRUNCATE TABLE(立刻释放,但不可回滚、不走 binlog) - 日常运维中,更稳妥的做法是建分区表,按时间字段分区,直接
DROP PARTITION











