innodb_flush_neighbors 控制innodb刷脏页时是否批量刷相邻脏页,开启后利用空间局部性合并i/o,在hdd上有益但在ssd上易浪费带宽;值为1刷同extent前后各一页,值为2刷整个extent(64页),生产环境通常设0。

innodb_flush_neighbors 是什么,为什么会影响刷脏页
这个配置控制 InnoDB 在刷一个脏页时,是否顺带把物理相邻的脏页也一起刷掉。开启后(值为 1 或 2),InnoDB 会检查该页所在 extent(区)里邻近的页是否也是脏页,如果是,就批量写入磁盘——本质是用空间局部性换 I/O 合并,减少随机写。
但它在 SSD 上几乎没收益,反而可能多刷不该刷的页,浪费 I/O 带宽;在传统 HDD 上才真正有用,因为顺序写比随机写快得多。
常见错误现象:SHOW ENGINE INNODB STATUS 里 Buffer pool hit rate 突降、Pages flushed 暴涨,但业务写入量没变,大概率就是它在“热心过头”。
什么时候该关掉 innodb_flush_neighbors
SSD/NVMe 环境下默认就该设为 0;MySQL 8.0.22+ 在检测到非旋转介质时会自动禁用,但旧版本或某些虚拟化环境(如云盘挂载为 block device)可能仍启用,必须手动干预。
实操建议:
- 先确认存储类型:
SELECT @@innodb_flush_neighbors和cat /sys/block/*/queue/rotational(Linux 下,值为 0 表示 SSD) - 动态修改(立即生效,但重启后恢复):
SET GLOBAL innodb_flush_neighbors = 0 - 永久生效:在
my.cnf的[mysqld]段落加一行innodb_flush_neighbors = 0 - 注意:该变量不能在只读实例或 Group Replication 的从节点上动态设置,需改配置文件后重启
设成 1 和 2 有啥区别
值为 1 时,只刷同 extent 内「前后各一页」(共最多 3 页);值为 2 时,刷整个 extent(通常 64 页)里的所有脏页。实际中几乎没人用 2 —— 它太激进,容易引发 I/O 尖刺,尤其当 extent 里只有 1–2 个脏页时,等于强制刷 64 页。
所以除非你跑在老式 HDD + 极高并发写 + 脏页分布极密集的 OLAP 场景,否则别碰 2。线上生产库见到 innodb_flush_neighbors = 2 基本可以视为配置失误。
性能影响明显:某次压测中,从 1 改为 2 后,Innodb_buffer_pool_write_requests 没变,但 Innodb_data_writes 翻了 4 倍,平均写延迟上升 300%。
调完之后怎么验证有没有效果
不能只看变量值,得观察真实刷页行为是否收敛。重点盯两个指标:
关键监控点:
- 每秒实际刷盘页数:
SHOW GLOBAL STATUS LIKE 'Innodb_pages_written',对比调整前后的 delta/s,下降 20%+ 且业务无抖动才算有效 - 刷页触发源:
SHOW ENGINE INNODB STATUS中的LOG和BUFFER POOL AND MEMORY段,关注pages made young和pages not made young是否稳定,突增说明刷页节奏被打乱 - 如果用了 Percona Server 或 MySQL 8.0+,可查
performance_schema.file_summary_by_event_name,过滤event_name = 'wait/io/file/innodb/innodb_log_file',看写日志频率是否同步下降
最容易被忽略的是:这个参数只影响 LRU 刷页和 checkpoint 刷页路径,对 innodb_max_dirty_pages_pct 触发的刷页无影响。所以即使关了它,脏页占比飙高照样会刷——别误以为“关了就不刷了”。










