应弃用 show engine innodb status 中的 buffer pool hit rate,因其仅为最近1000次读的滑动窗口统计;真实命中率需用 show global status 获取 innodb_buffer_pool_read_requests 和 innodb_buffer_pool_reads,按 (requests - reads) / requests × 100% 计算,并注意整除陷阱与异步 resize 导致的数据波动。

别信 SHOW ENGINE INNODB STATUS 里那个 Buffer pool hit rate,它只是最近 1000 次读的滑动窗口计数,不能反映真实缓存效率,生产环境必须弃用。
用 SHOW GLOBAL STATUS 算出准确命中率
真正该盯的两个变量是 Innodb_buffer_pool_read_requests(所有逻辑读请求)和 Innodb_buffer_pool_reads(真正从磁盘读页的次数)。命中率公式是:(requests - reads) / requests * 100%。
- 必须用
SHOW GLOBAL STATUS LIKE 'Innodb_buffer_pool_read%'直接查,避免通过INFORMATION_SCHEMA.INNODB_METRICS或 Performance Schema 子查询——后者容易因并发导致两个值取样时间点错位 - 整除陷阱要防:MySQL 默认整数除法会截断,建议显式转浮点或用
ROUND(..., 2) - 推荐一行查完:
SELECT ROUND((1 - b.Variable_value/a.Variable_value) * 100, 2) AS hit_rate FROM performance_schema.global_status a JOIN performance_schema.global_status b ON a.Variable_name = 'Innodb_buffer_pool_read_requests' AND b.Variable_name = 'Innodb_buffer_pool_reads';
命中率高 ≠ 没 I/O 压力
99.2% 看着很美,但如果 Innodb_buffer_pool_reads 每秒稳定在 50+,说明仍有大量热数据没进缓存,或 SQL 在反复回表、排序、扫描。
- 结合
sys.statement_analysis查SUM_ROWS_EXAMINED高但SUM_ROWS_SENT低的语句,大概率是扫了千行只返几行 - 用
EXPLAIN FORMAT=JSON看disk_reads字段(MySQL 8.0.22+),提前预估单条语句磁盘压力 - 观察
SHOW ENGINE INNODB STATUS\G中 BUFFER POOL AND MEMORY 段的Pages read和Pages created:前者远高于后者,说明读放大严重
刚调大 innodb_buffer_pool_size 后数据不准
执行 SET GLOBAL innodb_buffer_pool_size = ... 后,InnoDB 是异步 resize 的,Innodb_buffer_pool_pages_total 和 Innodb_buffer_pool_pages_free 会持续波动至少 5 分钟。
- 此时采集的
Innodb_buffer_pool_read_requests分母不稳定,算出的命中率会偏低失真 - 等
Innodb_buffer_pool_pages_free不再剧烈下降、且连续两分钟变化 - 冷启动阶段(MySQL 刚启)命中率天然偏低,前 30 分钟不具参考价值,重点看稳定运行后的趋势
最易被忽略的一点:命中率只是入口指标,真正卡顿往往藏在单条 SQL 的物理读行为里——与其盯着百分比,不如定期抓 Innodb_buffer_pool_reads 的每秒增量和对应慢日志里的执行计划。











