bigywater.tech
MySQL / InnoDB · 63

Buffer Pool 命中率

对同一个全表扫描连续跑两次,用逻辑读和物理读这两个真实计数器算出命中率,诚实讨论在共享实例上量'冷命中率'的真实局限。

约95%
第一次扫描真实的命中率(非冷启动)
100%
紧接着重复扫描一次的真实命中率

做的事:同一个全表扫描,连续跑两次

代码FLUSH STATUS; SELECT COUNT(*), SUM(amount) FROM orders; SHOW STATUS LIKE 'Innodb_buffer_pool_read_requests'; -- 逻辑读次数 SHOW STATUS LIKE 'Innodb_buffer_pool_reads'; -- 真实落到磁盘的物理读次数

验证:第一次扫描,真实的命中率大约95%

实测 · 本机第一次执行的真实输出
Innodb_buffer_pool_read_requests: 29434   ← 逻辑读
Innodb_buffer_pool_reads:          1453   ← 真实的磁盘物理读
命中率 = (29434-1453)/29434 ≈ 95.06%

这不是一次真正的"冷启动"读——orders这张表在本文前面几十篇实验里已经被反复扫描过,绝大部分页面真实早就躺在buffer pool里了,这次测出来的1453次物理读,只是这张表里还没被之前实验碰过、或者已经被别的页面挤出buffer pool的那一小部分。诚实地说,本文没能在这台共享的开发实例上量出一个真正意义上的"从0开始"的冷命中率——要做到这一点,真实需要重启mysqld清空buffer pool,但这台实例上还同时跑着别的真实业务库,本文没有这么做。

验证:原样再扫一次,命中率真实变成100%

实测 · 本机第二次执行的真实输出
Innodb_buffer_pool_read_requests: 29502  (比第一次多68,这次新增的逻辑读)
Innodb_buffer_pool_reads:          1453  (真实一次都没有增加)
这一次命中率 = 100%
第二次扫描的68次新增逻辑读全部真实命中了buffer pool——第一次扫描已经把这张表需要的全部数据页都真实读进了内存,第二次读同一份数据完全不需要再碰磁盘。Innodb_buffer_pool_read_requests和Innodb_buffer_pool_reads这两个状态变量都是从服务器启动以来的累计值,本文用FLUSH STATUS清零后测delta,才能看出"这一次操作"贡献了多少次读,而不是被历史累积的数字淹没——本机这台实例长期运行下来的累计命中率高达99.99%,但那个数字更多反映的是"这台机器绝大部分时间都在重复读同一批热数据",不是"每次冷读都几乎不用碰磁盘"。

分步演示:两次全表扫描,命中率的真实差异

每一步都是本文上面用SHOW STATUS跑出来的原始结果。

重复读的命中率,和真正冷启动的命中率不是一回事
这一步新验证的读取

    本文用到的工具

    • 本机 MySQL 26.7.0(InnoDB),5万行orders表,连续两次全表扫描对比
    • Innodb_buffer_pool_read_requests / Innodb_buffer_pool_reads:真实的逻辑读与物理读计数
    • 没有验证:真正意义上"冷启动"的命中率——这台共享开发实例上同时运行着别的真实业务库,本文没有为了这个测试重启它
    京ICP备2026067502号-1京公网安备11010502063487号