缓冲池:页置换、脏页与预读

kp-022 · 存储引擎与日志核心约 25 分钟已校对

前置知识点

前置:

相关知识点

相关:

学习进度:

一句话定义

缓冲池(Buffer Pool)是数据库在内存中开辟的页缓存:读写都以页为单位经它中转,命中率决定「一次查询是内存速度还是磁盘速度」,其置换算法与脏页刷盘节奏是数据库性能的地基。

为什么重要

前置知识

kp-019(页的概念)、kp-020(脏页与 WAL 的关系)。

核心概念

原理与机制

朴素 LRU 在数据库里有两个缺陷:全表扫描时,扫过的页「刚被访问」而挤掉真正热的数据页(一次性污染);热点页每次访问都移动链表头部,锁竞争高。InnoDB 的 midpoint LRU 把新读入页先放「young/old 交界」,在 old 区驻留超过 innodb_old_blocks_time(默认 1 秒)且再次被访问才晋升 young——全表扫的页只走 old 区,热数据不被冲刷。脏页管理:后台线程按 innodb_io_capacity 与 LSN 推进节奏(自适应 flushing)刷脏,令 checkpoint 平滑推进;若刷脏跟不上(IO 打满),用户线程会被强制参与刷盘,表现为周期性写入毛刺。命中率观测:Innodb_buffer_pool_read_requests / (read_requests + reads),工程红线一般 99% 以上。

实例或案例

夜间报表大查询把订单表热页挤出缓冲池,早高峰交易接口 P99 飙升。治理路径:报表读走只读副本(隔离缓存);必须同库时用低优先级会话限制并发,或(PG)为报表建独立表空间/使用 pg_prewarm 固化热表。另一案例:缓冲池 8GB 而热数据仅 2GB 但命中率仅 97%,排查发现大量 SELECT * 回表随机读——优化索引(kp-011)后命中率回升 99.8%,比扩内存便宜得多。

常见误区

自测题

  1. 为什么脏页刷盘必须「先 WAL 后数据页」? 答:若先刷数据页再覆盖对应 WAL,崩溃发生在两步之间时,既无法用日志重放(日志已丢)页也处于半新半旧状态,违反持久性。
  2. midpoint LRU 如何防御全表扫描污染? 答:新页先入 old 区,只有驻留超阈值且再次被访问才晋升 young,一次性扫描页不会驱逐真正的热页。

公式或模型

命中率:hit_ratio = hits / (hits + misses);miss 代价比 ≈ t_disk / t_mem(HDD 约 10⁵ 倍、SSD 约 10²~10³ 倍),故命中率差 1 个百分点的 IO 放大 = t_disk × Δmiss / t_mem,远超直觉。

图示

young 区(热页) old 区(新读入页 / 全表扫页) midpoint(5:5 分界) old 区驻留超 1s 且再次访问 → 晋升 young 淘汰出池
midpoint LRU:新页从 midpoint 进入,只有「二次访问」才晋升热区

直观类比

缓冲池像餐厅后厨的备菜台:常用食材放在台面(young 区),新进的先放台面边缘(old 区)观察——确实常用才转正;一次性大单(全表扫描)进的一整车货堆在边缘,绝不挤掉台面上的常客。脏盘子(脏页)必须按规矩先记完流水账(WAL)才能端走洗(刷盘)。

与其他知识点的关系

kp-009 的树高层常驻与 kp-022 的命中率互为因果;kp-020 的检查点节奏在此执行;kp-013 的代价模型里「页在不在缓存」直接影响 IO 代价权重。

延伸阅读