直接看innodb_buffer_pool_pages_dirty(持续超70%总页数)和innodb_buffer_pool_wait_free(非零),再结合innodb_data_fsyncs与innodb_data_writes比值偏低,可确认刷脏页压力大;innodb_io_capacity设过低会限制后台刷页速度,导致脏页堆积、强制同步及瞬时卡顿。

查哪些状态变量能反映刷脏页频率和延迟
直接看 Innodb_buffer_pool_pages_dirty 和 Innodb_buffer_pool_wait_free:前者持续高于 buffer pool 总页数的 70%,说明脏页积压;后者非零,代表线程正在排队等空闲页——这是刷脏页跟不上写入节奏的明确信号。
再配合 Innodb_data_fsyncs 与 Innodb_data_writes 的比值。如果 Innodb_data_fsyncs 远低于 Innodb_data_writes(比如比值
还要盯住挂起类指标:Innodb_data_pending_writes、Innodb_os_log_pending_fsyncs,只要任一值 > 0 且持续增长,就是 I/O 队列已满、刷脏页被阻塞的铁证。
为什么只看 SHOW GLOBAL STATUS 不够准
SHOW GLOBAL STATUS 是累计值,无法体现“瞬时压力”。比如 Innodb_buffer_pool_pages_dirty 看起来是 5000,但你不知道它是稳定在 5000,还是每分钟在 2000–8000 之间剧烈震荡——而后者才是导致“抖一下”的元凶。
必须做差值计算:每 5–10 秒采集一次,用脚本算单位时间内的变化量。例如:
SELECT
(VARIABLE_VALUE - @prev_dirty) / 10 AS dirty_pages_per_sec,
(VARIABLE_VALUE - @prev_fsyncs) / 10 AS fsyncs_per_sec
FROM performance_schema.global_status
WHERE VARIABLE_NAME IN ('Innodb_buffer_pool_pages_dirty', 'Innodb_data_fsyncs');
这样才能量化出真实的刷新吞吐能力是否达标。
Innodb_buffer_pool_instances 设置不当会掩盖真实延迟
当 Innodb_buffer_pool_size > 8GB 且 Innodb_buffer_pool_instances 仍为默认的 1 时,所有刷脏页请求都争抢同一把 buf_flush_list_mutex 锁。这时你看到的 Innodb_buffer_pool_wait_free 高,并不全是磁盘慢,很可能是锁排队造成的假性延迟。
检查当前配置:
- 运行
SHOW VARIABLES LIKE 'innodb_buffer_pool_instances'; - 确认
innodb_buffer_pool_size是innodb_buffer_pool_instances × innodb_buffer_pool_chunk_size的整数倍(否则部分实例不生效) - SSD 环境下,建议设为
min(8, CPU核心数);32GB buffer pool 至少配 4 个实例
改完后需重启 MySQL 才生效,动态 SET 不起作用。
如何区分是刷脏页慢,还是 redo log 写满连坐
两者都会导致卡顿,但触发机制完全不同。关键看 SHOW ENGINE INNODB STATUS\G 中 LOG 段的两个值:
-
Log sequence number和Last checkpoint at的差值,若超过innodb_log_file_size × innodb_log_files_in_group的 80%,说明 redo log 快满了,即将强制刷脏页 - 同时观察
Innodb_os_log_written增速:如果每秒增长超 10MB,而innodb_log_file_size只有 128MB,那平均每 12 秒就可能触发一次 checkpoint,造成周期性抖动 - 此时
Innodb_buffer_pool_pages_dirty会突然断崖式下降,不是因为刷得快,而是被“连坐”强制清空
真正难排查的是混合场景:buffer pool 实例数不足 + innodb_io_capacity 设太低 + redo log 太小,三者叠加会让监控曲线看起来像噪音,必须逐项隔离验证。











