Page Cleaner 卡住的明确标志是错误日志中高频出现“InnoDB: page_cleaner: XXXms intended loop took YYYYms”,且 SHOW ENGINE INNODB STATUS 中 Modified db pages 长期 >3000、Free buffers 为 0、每轮 flushed 仅个位数;需确保 innodb_page_cleaners 与 innodb_buffer_pool_instances 相等且 ≥4,调优时应降低 innodb_lru_scan_depth(如设为256)、SSD 上禁用 innodb_flush_neighbors(设为0)、正确配置 innodb_io_capacity(SSD=4000/HDD=200),并避免 redo 日志与数据文件共盘、tmpdir 占用根分区等硬件级争抢。

直接看错误日志里有没有 InnoDB: page_cleaner: XXXms intended loop took YYYYms 这类记录,有就说明 Page Cleaner 已失能 —— 不是“可能有问题”,而是正在拖垮写性能。
怎么确认 page_cleaner 真的卡住了
别只扫一眼日志就动手调参。先验证三件事:
- 日志是否高频重复?比如每秒或每几秒都出现
InnoDB: page_cleaner: 1000ms intended loop took 5248ms—— 偶发一次可能是抖动,持续出现就是系统性阻塞 - 执行
SHOW ENGINE INNODB STATUS\G,在BUF BUFFER POOL AND MEMORY小节里查:Modified db pages是否长期 > 3000、Free buffers是否为0 - 对比日志中
flushed=N和当前Modified db pages:如果脏页总数 5000+,但每轮只刷个位数(如flushed=3),说明刷脏能力被严重抑制
为什么 innodb_page_cleaners 和 innodb_buffer_pool_instances 必须对齐
MySQL 8.0 默认启用多 Page Cleaner 线程,但它不会自动适配 buffer pool 实例数。错配会导致线程争抢或闲置:
- 运行
SELECT @@innodb_page_cleaners, @@innodb_buffer_pool_instances; - 理想值是两者相等且 ≥ 4(例如都设为 8)
- 常见陷阱:
innodb_page_cleaners = 8但innodb_buffer_pool_instances = 1→ MySQL 会自动截断为 1,8 个线程只剩 1 个干活 - 反过来,
innodb_page_cleaners = 1但innodb_buffer_pool_instances = 8→ 单线程要轮询 8 个实例,loop 耗时必然飙升
哪些参数调错会让 page_cleaner 更慢
很多团队调大 innodb_buffer_pool_size 或 innodb_io_capacity 反而加重问题,关键在组合逻辑:
-
innodb_lru_scan_depth:默认 1024,实际工作量 = 该值 ×innodb_buffer_pool_instances。若 buffer pool 实例为 8,那每秒要扫描 8192 页 —— SSD 也扛不住。建议从 256 起步,观察Free buffers是否稳定 > 0 -
innodb_io_capacity和磁盘类型强绑定:innodb_io_capacity = 4000(SSD)、200(HDD),设高了没 IO 支撑就是空转;设低了则主动压低刷脏节奏,脏页越积越多 -
innodb_flush_neighbors = 1在 SSD 上是负优化:它本为 HDD 减少寻道设计,SSD 上反而引发无效邻近页读写,放大 I/O 压力。务必设为0 -
innodb_adaptive_flushing必须为ON,否则innodb_io_capacity几乎不生效
硬件和路径配置比参数更致命
参数调得再准,如果下面这些地方没理清,page_cleaner 依然会卡死:
-
ib_logfile*和数据文件混在同一块物理盘上:刷脏(data)和写 redo 日志(log)争抢 I/O,iostat -x 1会看到%util长期 > 95% -
tmpdir指向根分区(如/tmp):大排序、GROUP BY 生成的磁盘临时表,直接挤占 page_cleaner 的写带宽 - 没用
iotop -p $(pgrep mysqld)确认 mysqld 进程的真实写速率 —— 很多时候不是 MySQL 慢,而是 OS 层面写请求被 block 在队列里 - 误以为增大
innodb_buffer_pool_size就能缓解:写入密集场景下,buffer pool 越大,脏页总量越多,page_cleaner 压力反而更大
最常被忽略的一点:page_cleaner 报警本身不报错、不中断服务,但它持续存在时,Modified db pages 会缓慢爬升,直到某次 checkpoint 触发集中刷脏,瞬间打爆磁盘 I/O,接着就是连接池耗尽、查询超时、甚至 mysqld 强制重启 —— 它不是起点,而是系统窒息前的最后一声喘息。


















