mysql脏页落盘是后台线程按策略异步调度的过程,源于innodb只改内存页并写redo log,为避免实时刷盘拖慢tps而延迟处理;其触发有四大硬性场景,且受page cleaner水位线与io能力动态调控。

MySQL 的脏页落盘不是“要不要做”的问题,而是“什么时候、以什么节奏做”的调度问题;它不发生在 SQL 执行完的瞬间,而由后台线程按策略异步推进。
脏页是怎么产生的,为什么不能立刻刷盘
InnoDB 更新数据时,默认只改 buffer pool 里的内存页,并写入 redo log(WAL 机制)。这时内存页和磁盘文件内容不一致,就变成“脏页”。立刻刷盘会严重拖慢响应——每个 UPDATE 都等磁盘 IO,TPS 直接崩掉。所以 MySQL 把刷盘交给后台线程,让客户端快速拿到“成功”响应。
常见错误现象:UPDATE 或 INSERT 突然变慢、出现明显“抖动”,往往不是语句本身慢,而是被后台刷脏页阻塞了。
- 脏页只存在于
buffer pool中,flush list是专门维护这些脏页描述信息的链表,刷盘优先从这里取 - 一个页可以同时在
lru list(表示最近访问)和flush list(表示已修改)中 -
innodb_flush_log_at_trx_commit=1控制的是redo log是否同步刷盘,和脏页刷盘是两件事
四种明确触发 flush 的实际场景
刷脏页不是随机行为,有四个硬性触发点,其中两个会直接卡住更新:
-
redo log写满:环形缓冲区的write pos追上checkpoint,所有写请求阻塞,必须把 checkpoint 前的所有脏页刷完才能继续 —— 这是最危险的“抖动”来源 -
buffer pool空间不足:LRU 淘汰到脏页时,必须先刷盘再释放内存,大查询或全表扫描极易触发 - 脏页比例超阈值:
innodb_max_dirty_pages_pct默认 90%,超过后page cleaner线程加快异步刷盘频率 - MySQL 正常关闭:
innodb_fast_shutdown=0时强制刷完所有脏页,否则可能延长关机时间
page cleaner 线程怎么控制刷盘节奏
这个后台线程不靠“猜”,而是靠两个水位线和 I/O 能力参数动态调节:
-
innodb_max_dirty_pages_pct_lwm(默认 10%)是低水位线:低于它,page cleaner几乎不干活 -
innodb_max_dirty_pages_pct(默认 90%)是高水位线:超过它,进入“紧急模式”,刷盘速率逼近innodb_io_capacity上限 -
innodb_io_capacity不是理论最大值,而是你磁盘真实能承受的 IOPS(如 SATA SSD 设为 200,NVMe 可设 2000+);设太高会导致 IO 打满,设太低会让脏页堆积 - 每秒一次定时检查,但真正刷多少页,由当前脏页占比、距离水位线差值、以及
innodb_io_capacity共同决定
容易被忽略的性能陷阱
很多人调优只盯着 innodb_buffer_pool_size,却忘了脏页刷盘是跨模块协作的结果:
- redo log 太小(
innodb_log_file_size×innodb_log_files_in_group)会频繁触发 checkpoint,等于变相增加刷脏压力 - 设置
innodb_flush_log_at_trx_commit=2能缓解 redo log 刷盘压力,但不会减少脏页数量,该刷的页一个不少 - 监控时别只看
Innodb_buffer_pool_pages_dirty,要结合Innodb_buffer_pool_wait_free—— 后者非 0 就说明 buffer pool 已经开始抢内存了 - 用
SHOW ENGINE INNODB STATUS查看LOG和BUFFER POOL AND MEMORY部分,比查变量更反映实时压力
真正难的不是知道“什么时候刷”,而是判断“现在是不是快刷不过来了”——这需要把 redo log 使用率、脏页占比、I/O 等待、buffer pool 命中率这几项指标串起来看。











