Skip to content

InnoDB 刷脏页机制

一、 问题现象:SQL 语句突然变慢

  • 场景:平时执行很快的 SQL 更新语句,偶尔会变得异常缓慢,持续时间短且难以复现,如同数据库“抖动”了一下。
  • 根源猜想:这种“抖动”通常与 InnoDB 将内存中的脏页 (Dirty Page) 刷写到磁盘(Flush)的操作有关。

二、 理解 WAL 与脏页

  1. WAL (Write-Ahead Logging)
    • InnoDB 更新时,先修改内存中的数据页,然后将更改记录顺序写入 redo log(重做日志)。
    • 写完 redo log 后即可返回成功,避免了立即向数据文件进行随机写,提高了更新性能。
  2. 脏页 (Dirty Page):内存 (Buffer Pool) 中的数据页,其内容已被修改,但尚未同步到磁盘上的数据文件,与磁盘版本不一致。
  3. 干净页 (Clean Page):内存中的数据页,其内容与磁盘版本一致。
  4. 刷脏 (Flush):将内存中的脏页写回磁盘数据文件,使其变回干净页的过程。

三、 触发刷脏页 (Flush) 的四种场景

  1. Redo Log 写满

    • Redo log 是循环写入的。当日志写满,新的更新无法写入时,系统必须停止所有更新,强制将 Checkpoint 之前的 redo log 对应的脏页刷到磁盘,以释放 redo log 空间。
    • 性能影响严重,导致写操作完全阻塞,TPS 跌零。InnoDB 会尽量避免此情况。
  2. 内存 (Buffer Pool) 不足

    • 当需要加载新数据页到内存,但 Buffer Pool 已满时,需要淘汰最久未使用的内存页。
    • 如果要淘汰的是脏页必须先将其刷到磁盘变成干净页后才能淘汰。
    • 性能影响常见,会拖慢触发淘汰操作的查询语句的响应时间。
  3. 系统空闲时

    • MySQL 检测到系统负载较低时,会见缝插针地刷一部分脏页,以降低脏页比例,减少未来高峰期压力。
    • 性能影响:对业务影响较小。
  4. MySQL 正常关闭时

    • 为了保证数据一致性并加快下次启动速度,关闭时会刷写所有脏页。
    • 性能影响:非运行时问题。

四、 InnoDB 刷脏页的控制策略

  • 目标:平衡刷脏页的速度,既要避免 Redo Log 写满和脏页比例过高,又不能过度消耗 I/O 影响正常业务请求。

  • innodb_io_capacity

    • 告知 InnoDB 主机的磁盘 IOPS 能力。定义了 InnoDB 在需要时最大能以多快的速度刷脏页。
    • 重要性:设置过低会导致刷脏页跟不上产生速度,脏页堆积;设置过高可能在全力刷时打满磁盘 I/O。建议根据实际磁盘性能(如用 fio 测试)设置。
  • 自适应刷脏速度 (Adaptive Flushing)

    • InnoDB 根据两个核心因素动态调整实际刷脏速度:
      • 脏页比例:当前脏页占 Buffer Pool 总页数的百分比 (M)。与 innodb_max_dirty_pages_pct (默认 75%) 比较,计算出一个因子 F1 (M 越接近上限,F1 越大,最大 100)。
      • Redo Log 写入速度/距离:当前 LSN 与 Checkpoint LSN 之间的差距 (N)。N 越大(表示待写入磁盘的日志越多),计算出的因子 F2 越大。
    • 最终速度:取 F1 和 F2 中的较大值 (R),实际刷脏速度 ≈ innodb_io_capacity * R%
  • 监控脏页比例

    • 通过 Innodb_buffer_pool_pages_dirty / Innodb_buffer_pool_pages_total 计算。
    • 应关注此比例,避免其经常接近 innodb_max_dirty_pages_pct 设定的上限。
  • 邻居页刷写 (innodb_flush_neighbors)

    • 机制:当刷写一个脏页时,如果其物理上相邻的数据页也是脏页,则顺便一起刷掉(可能蔓延)。
    • HDD 时代有益,将多次随机 I/O 合并为更连续的 I/O,提升性能。
    • SSD 时代建议关闭 (设为 0)。SSD 的随机 I/O 性能好,关闭此特性可以让必要的脏页更快刷完,减少单次“抖动”时间。MySQL 8.0 默认已为 0。

五、 总结

  • MySQL 的“抖动”通常是 InnoDB 刷脏页 操作导致的,该操作会占用 I/O 资源,并在特定情况下(如淘汰脏页)阻塞查询。
  • 理解刷脏页的触发时机(Redo Log 满、内存不足最关键)和 InnoDB 的控制策略(基于 innodb_io_capacity、脏页比例、Redo Log 距离)有助于诊断和优化性能。
  • 关键配置:合理设置 innodb_io_capacity,并根据磁盘类型调整 innodb_flush_neighbors
  • 监控:关注脏页比例,避免其长时间过高。
  • WAL 与 Change Buffer 对比:Redo Log (WAL) 主要节省随机 I/O,Change Buffer 主要节省随机 I/O。