MySQL:Fuzzy Checkpoint
一、 为什么需要“模糊(Fuzzy)”?对比 Sharp Checkpoint
- Sharp Checkpoint(全量检查点):顾名思义,要求将 Buffer Pool 中所有的脏页一次性全部刷新到磁盘。
- 触发时机:通常只在数据库正常关闭(Clean Shutdown)时发生。
- 致命缺点:如果在系统运行期间执行全量刷盘,由于磁盘 I/O 吞吐量的物理限制,会导致系统瞬间产生极大的 I/O 阻塞,用户层面的 DML 操作会全部卡死(Stop The World)。
- Fuzzy Checkpoint(模糊检查点):每次只刷新一部分脏页到磁盘,并且是异步执行的。
- 核心优势:将密集的 I/O 操作打散到系统运行的生命周期中,对用户线程的影响降到最低,保证了数据库持续的高吞吐量。
二、 Fuzzy Checkpoint 的四大触发场景与底层逻辑
InnoDB 根据不同的系统压力状态,设计了四种触发 Fuzzy Checkpoint 的策略,以应对 Buffer Pool 和 Redo Log 的空间管理。
1. Master Thread Checkpoint(定时任务触发)
这是系统在正常运行且压力平稳时,最基础的脏页刷盘机制。在 MySQL 5.6 之后的版本,这部分工作逐渐交由独立的Page Cleaner Thread完成,但核心的时间驱动逻辑一致。
- 1秒循环(后台轮询):InnoDB 会判断当前系统的 I/O 压力。如果过去一秒发生的 I/O 次数低于阈值,说明磁盘比较空闲,系统会主动将一定数量(如
innodb_io_capacity配置的百分比)的脏页刷入磁盘。 - 10秒循环(强制执行):无论系统 I/O 压力如何,每隔 10 秒,InnoDB 都会强制将一批脏页(最多为
innodb_io_capacity的值)刷新到磁盘。
2. FLUSH_LRU_LIST Checkpoint(Buffer Pool 空间不足触发)
Buffer Pool 是有限的内存空间。当用户发起新的查询,需要将磁盘上的数据页加载到内存时,如果 Buffer Pool 中没有足够的空闲页(Free Page),就需要淘汰(Evict)部分旧页。
- 触发逻辑:InnoDB 维护了一个 LRU(最近最少使用)链表。后台线程会监控 LRU 链表尾部,以保证系统中始终有大约 100 个以上的空闲页。
- 刷盘动作:如果 LRU 链表尾部被淘汰的数据页恰好是脏页,则必须先触发 Checkpoint 将其同步到磁盘,然后才能将该内存页清空变为 Free Page。
3. Async/Sync Flush Checkpoint(Redo Log 空间不足触发 - 最危险的情况)
Redo Log 是一个大小固定的环形(Circular)日志文件组。写入时指针不断向前推进,如果写入速度过快,“写指针”追上了“Checkpoint 指针”,就会发生日志覆盖。
为了防止未落盘的数据对应的 Redo Log 被覆盖,InnoDB 设定了水位线(Watermark):
- 定义
checkpoint_age = Redo Log 写入的最新 LSN - Checkpoint LSN。 - Async Watermark(异步水位线,通常为日志总容量的 75%):当
checkpoint_age达到此值时,触发异步刷盘。此时后台线程开始疯狂地将脏页刷入磁盘,试图推进 Checkpoint LSN,但此时不会阻塞用户的 DML 线程。 - Sync Watermark(同步水位线,通常为日志总容量的 90%):如果异步刷盘速度跟不上脏页产生速度,
checkpoint_age达到此值时,触发同步刷盘。此时 InnoDB 会强行阻塞所有用户的写操作(DML),集中所有 I/O 资源进行脏页刷盘,直到 Redo Log 腾出足够空间。这是生产环境中必须极力避免的性能抖动点。
4. Dirty Page too much Checkpoint(脏页比例过高触发)
为了防止系统在崩溃时积累过多的脏页导致恢复时间(Crash Recovery Time)过长,InnoDB 设定了脏页比例的阈值。
- 触发逻辑:由参数
innodb_max_dirty_pages_pct控制(默认值为 75%,早期版本为 90%)。 - 刷盘动作:当 Buffer Pool 中的脏页占比超过这个百分比时,InnoDB 会强制触发 Checkpoint,以最快速度(
innodb_io_capacity指定的最高速率)将脏页刷盘,直到比例下降到阈值以下。
三、 Fuzzy Checkpoint 是如何推进 LSN 的?
每次 Fuzzy Checkpoint 发生时,InnoDB 到底把 Checkpoint LSN 推进到了哪个值?
- Flush List(刷盘链表):InnoDB 内部维护了一个 Flush List,所有脏页不仅存在于 LRU 链表中,同时也存在于 Flush List 中。
- 按 LSN 排序:Flush List 中的脏页是严格按照该页第一次被修改时产生的 LSN(oldest_modification)从小到大排序的。
- 提取最小值:每次 Checkpoint 发生时,后台线程会将一批脏页刷盘。刷盘完成后,系统会去读取 Flush List 队首那个脏页的 LSN。
- 更新标记:因为队首的 LSN 是当前所有未刷盘脏页中最小的 LSN。这就意味着,小于这个 LSN 的所有数据修改,已经全部安全地落入磁盘。InnoDB 随即将这个最小的 LSN 写入 Redo Log Header,这就是新的 Checkpoint LSN。
