innodb缓存命中率必须用show global status实时获取innodb_buffer_pool_reads和innodb_buffer_pool_read_requests两值后手动计算:(1 - reads/requests) × 100,避免performance_schema子查询导致的时间错位;命中率低时优先排查全表扫描等污染缓冲池的sql,而非盲目调大buffer_pool_size。

用 SHOW GLOBAL STATUS 算出真实命中率
别用 performance_schema.global_status 子查询拼接两个变量——并发下 Innodb_buffer_pool_reads 和 Innodb_buffer_pool_read_requests 可能取自不同时间点,结果失真。直接执行:
SHOW GLOBAL STATUS LIKE 'Innodb_buffer_pool_read%';
拿到两行实时值后手动代入公式:(1 - Innodb_buffer_pool_reads / Innodb_buffer_pool_read_requests) * 100。这是唯一能避开时间错位的可靠方式。
- 如果返回空,说明 MySQL 版本低于 5.6 或 Performance Schema 被禁用(但
SHOW GLOBAL STATUS始终可用) -
Innodb_buffer_pool_reads是从磁盘读页的真实次数,每秒持续 >50 就值得警惕 - 不要只看百分比:99.2% 很高,但如果
Innodb_buffer_pool_reads每秒涨 120,说明仍有大量热数据没进缓存
命中率低 ≠ 一定要调大 innodb_buffer_pool_size
Buffer Pool 命中率持续低于 95%,第一反应不是加内存,而是查有没有 SQL 在“污染”缓冲池。全表扫描、ORDER BY 无索引、JOIN 缺索引都会把冷数据页硬塞进 LRU 链表,挤走真正热的数据。
- 运行
SHOW ENGINE INNODB STATUS\G,翻到 BUFFER POOL AND MEMORY 段,看Pages read是否远高于Pages created—— 这是读放大的明确信号 - 用
sys.statement_analysis查SUM_ROWS_EXAMINED高但SUM_ROWS_SENT低的语句,典型如SELECT * FROM orders WHERE status = 'pending'却没给status建索引 - MySQL 8.0.22+ 可用
EXPLAIN FORMAT=JSON看disk_reads字段,提前预估单条语句的物理读压力
调整 innodb_buffer_pool_size 的实操边界
这个参数不是越大越好,盲目设到 90% 物理内存可能触发系统 OOM。真实生产环境要按服务器角色和负载类型分档处理:
- MySQL 专用服务器(无其他服务):物理内存 ≤16G → 设为 60%~70%;16G~64G → 70%~80%;>64G → 80%~85%,但必须预留 ≥2G 给 OS
- 混合部署(如共存 Nginx、Java 应用):先用
free -m看可用内存,减去其他进程 RSS 总和,再分配剩余的 70%~80% - 修改后必须重启 MySQL 生效,不能用
SET PERSIST动态改(8.0.22+ 支持但仅限部分场景,且重启后才真正加载) - 调完立刻跑 15 分钟以上观察:命中率是否稳定 ≥99%,同时
Innodb_buffer_pool_reads是否明显下降
别忽略 innodb_old_blocks_pct 和 innodb_old_blocks_time
Buffer Pool 内部用分代 LRU 管理页面,innodb_old_blocks_pct(默认 37%)和 innodb_old_blocks_time(默认 1000ms)共同控制冷热分离。如果命中率上不去,很可能是全表扫描把热数据冲跑了。
- 当发现
Innodb_buffer_pool_reads高但Innodb_buffer_pool_pages_misc(管理开销页)也异常高,说明 LRU 淘汰太频繁,可尝试调低innodb_old_blocks_pct到 25,让新页更难进入 Old 区 - 若应用有周期性批量导入或报表任务,建议在任务前临时设
innodb_old_blocks_time = 0,让预读页快速淘汰,避免污染 Young 区 - 这两个参数不需重启,
SET GLOBAL即生效,但要注意它们影响的是页面驻留策略,不是缓存容量本身
真实瓶颈往往藏在 Innodb_buffer_pool_reads 的绝对值和变化节奏里,而不是那个看起来漂亮的百分比。一次命中率下跌,可能对应着一条没走索引的 UPDATE,也可能是一次没控制范围的 mysqldump 全库导出。











