page cleaner thread took too long 是明确的io压力告警,表明page_cleaner线程未能在1000ms内完成脏页刷写,根本原因是磁盘写入能力跟不上脏页生成速度,导致脏页堆积、buffer pool压力升高及查询延迟上升。

Page Cleaner Thread took too long 是什么信号
这句日志不是警告,是明确的 IO 压力告警:InnoDB 的 page_cleaner_thread 在规定时间内没完成脏页刷写任务。它说明后台刷脏逻辑被阻塞,根本原因通常是磁盘写入跟不上脏页生成速度,导致 Buffer Pool 中脏页持续堆积,进而抬高物理写压力、拖慢查询响应。
为什么 page_cleaner 会卡住
关键不在线程本身,而在它依赖的底层 IO 能力。常见真实原因包括:
-
innodb_io_capacity设得太低(比如 SSD 上还设 200),InnoDB 主动压低刷脏节奏,但业务写入量大,脏页越积越多 -
innodb_io_capacity_max没配或设得太小,突发脏页高峰时无法弹性提速,page_cleaner只能硬扛超时 - redo log 太小(
innodb_log_file_size过小),频繁 checkpoint 强制触发集中刷脏,形成 I/O 尖峰 - 磁盘本身已饱和:
iostat -x 1显示%util长期 >95% 或await>20ms,page_cleaner发出写请求后等不到磁盘响应 -
innodb_flush_neighbors = 1(默认)在 SSD 上反而降低效率,引发无效邻近页读写放大
怎么调参让 page_cleaner 跟上节奏
不能只改一个参数,必须组合调整:
- 先确认磁盘类型:SSD 就设
innodb_io_capacity = 4000,innodb_io_capacity_max = 12000;HDD 则分别设200和400 - 务必开启自适应刷新:
SET GLOBAL innodb_adaptive_flushing = ON,否则innodb_io_capacity几乎不生效 - 增大 redo log:单个
innodb_log_file_size至少 1G(innodb_log_files_in_group默认 2,总大小 2G 起),减少 checkpoint 频率 - SSD 环境下关闭邻近页刷新:
SET GLOBAL innodb_flush_neighbors = 0 - 观察
Innodb_buffer_pool_pages_dirty是否平稳回落 —— 忽高忽低说明刷脏节奏仍不匹配业务写入模式
容易被忽略的硬件与配置陷阱
很多团队调完参数发现没改善,问题常出在这些地方:
- redo log 文件和数据文件混在同一块物理盘上,刷脏和写日志争抢 IO —— 应把
ib_logfile*单独放到高速 NVMe 盘 -
tmpdir指向根分区(/tmp),大排序或 GROUP BY 生成大量磁盘临时表,进一步挤占 IO 带宽 - 监控只看 MySQL 内部指标,却没用
iotop -p $(pgrep mysqld)确认 mysqld 进程的真实写速率是否卡在瓶颈值 - 误以为调大
innodb_buffer_pool_size就能解决 —— 实际上如果写入密集,buffer pool 越大,脏页总量越多,page cleaner 压力反而更大
page_cleaner 的 timeout 不是孤立现象,它是磁盘 IO 能力、InnoDB 刷脏策略、redo log 设计三者失衡的交汇点。盯住 Innodb_buffer_pool_pages_dirty 和 iostat 的 await,比单纯调参更可靠。











