同一条 SQL 第一次慢、第二次快,不能只用“缓存了”三个字解释。InnoDB 缓存的是页,访问路径上的数据页、索引页是否已经在 Buffer Pool 里,直接影响一次查询要不要碰磁盘。
Buffer Pool 的核心不只是加速读取,还包括热点保护、淘汰策略、脏页管理和预读控制。理解它,才能解释缓存命中、偶发抖动、大扫描污染和刷盘压力之间的关系。
先把机制边界说清楚
Buffer Pool 是 InnoDB 最核心的内存结构,用来缓存数据页、索引页、自适应哈希索引等内容。InnoDB 读写数据的基本单位是页,SQL 快不快,很大程度取决于访问路径上的页是否已经在 Buffer Pool 里。
整体路径
上面这张图先看粗线条:宏观上,Buffer Pool 位于 SQL 执行和磁盘文件之间。查询先按索引路径定位需要的页,如果页在内存里就直接读;不在内存里才触发磁盘读。更新也先修改内存页,页变脏后再由后台线程刷回磁盘。
底层流程
底层拆解先看数据结构。「Buffer Pool」至少涉及下面几类结构:
- Buffer Pool Page:缓存的数据页或索引页。
- LRU 链表:管理冷热页,决定淘汰顺序。
innodb_buffer_pool_size默认 128MB,innodb_page_size默认 16KB。 - Flush List:管理脏页,配合 checkpoint 刷盘。脏页比例由
innodb_max_dirty_pages_pct控制(8.0 默认 90,5.7 是 75——这是个常被答错的版本差异)。 - Free List:保存空闲页,供新读入页面使用。
InnoDB 的 LRU 不是朴素 LRU,而是改良版 LRU(分代管理),这是高频追问点。它把 LRU 链表分成 young 区和 old 区:新读入的页先插到 old 区的头部,而不是直接进 young 区——这是第一道闸门,防止一次全表扫描把热点页全挤掉(预读失效/全表扫描污染)。第二道闸门是:一个 old 区的页只有在被再次访问、且**距离进入 old 区超过 innodb_old_blocks_time(默认 1000ms)**后,才会晋升到 young 区。innodb_old_blocks_pct 默认 37,表示 old 区占整个 LRU 的 3/8(young 占 5/8,不是「young 占 37%」)。这两道闸门配合,让一次大扫描读进来的冷页最多待够 1 秒就被淘汰,而真正被反复访问的热页能稳定留在 young 区。
再看完整执行流程:
- 执行器通过存储引擎访问索引或数据页。
- Buffer Pool 查找目标页是否命中。
- 命中则直接读取或修改内存页。
- 未命中则从磁盘读入,并放入 LRU 的合适位置。
- 脏页后续进入刷盘链路。
取舍与边界
版本差异上,MySQL 5.7 和 8.0 的 Buffer Pool 基本模型一致。8.0 在实例管理、预热、监控指标和自适应配置上更完善,但核心仍是页缓存、LRU、脏页刷盘和 checkpoint 协作。
Buffer Pool 的短板是容易被错误访问模式污染。大范围扫描、缺索引查询、报表 SQL 可能把热点页挤出去;Buffer Pool 再大,也挡不住无效扫描不断搬运冷数据。
典型问题:用机制化例子排查
缓存抖动可以用页的生命周期来看:一条查询把哪些页读进 Buffer Pool,这些页能停留多久,会不会被大扫描挤出去,脏页又什么时候要刷回磁盘。
可以落到这些动作:
- 先看 Buffer Pool 命中率、脏页比例和读写 IO,而不是直接加内存。
- 用索引和查询范围保护热点页,避免大扫描污染缓存。
- 核心库区分 OLTP 请求和报表请求,必要时读写或冷热隔离。
- 重启后可用 Buffer Pool dump/load 预热关键页面。
收束:缓存命中背后是页治理
Buffer Pool 不是泛泛的缓存层,而是 InnoDB 页的工作区。热点保护、淘汰、预读和脏页刷盘共同决定了 SQL 是碰内存还是碰磁盘。
关于十三Tech
我是十三,All in AI Agent 方向的架构师,专注 AI 工程实践。
我相信 AI 是程序员的最佳搭档,也希望帮助每一位开发者更好地驾驭 AI。
如果你想继续跟完这套「图解 MySQL」,欢迎关注公众号 「十三Tech」。后续会继续按机制、图解和实战排查这条线更新。

