真实buffer pool命中率应为(innodb_buffer_pool_read_requests - innodb_buffer_pool_reads) / innodb_buffer_pool_read_requests × 100%,需用show global status获取分子分母,避开冷启动和瞬时干扰,低于95%才需优化。

怎么算出真实的Buffer Pool命中率
别信 SHOW ENGINE INNODB STATUS\G 里那行 “Buffer pool hit rate 856 / 1000”——它只是过去60秒加权平均,抖动大、易误判。刚重启后前10分钟基本不准。
真正该用的公式是:(Innodb_buffer_pool_read_requests - Innodb_buffer_pool_reads) / Innodb_buffer_pool_read_requests * 100,结果低于95%才算真低;高于99%再调大 innodb_buffer_pool_size 基本白忙,还可能触发 swap。
- 执行
SHOW GLOBAL STATUS LIKE 'Innodb_buffer_pool_read%';拿到分子分母,别用SHOW STATUS(会重置计数器) - 采样周期至少5分钟,避开瞬时扫描干扰
- 如果
Innodb_buffer_pool_reads持续 > 50/秒,说明物理读已成瓶颈
为什么调大 innodb_buffer_pool_size 反而更糟
不是所有“设大了”都生效。MySQL 5.7.5+ 要求 innodb_buffer_pool_size 必须是 innodb_buffer_pool_chunk_size(默认128MB)的整数倍。设了个48G,但实例配了 innodb_buffer_pool_instances = 5,5 × 128MB = 640MB,48G ÷ 640MB = 76.8 → 非整数,MySQL 启动时自动向下截断,实际可能只用了47.5G,且错误日志只打 warning 不报错。
- 查真实生效值:
SELECT @@innodb_buffer_pool_size;,必须和配置文件一致才算成功 -
innodb_buffer_pool_instances不可动态改,设太小(如1)高并发下锁争用明显,设太大(如32)会让每个 instance 太小,LRU 局部性变差 - 推荐组合:总池大小 ÷ 128MB 得到 chunk 数,再除以 instance 数应为整数;例如48G ÷ 128MB = 384,设
instances = 8刚好每份48 chunk
缓存污染比内存小更致命
命中率卡在700–850、Free buffers = 0、但 youngs/s 极低(SELECT * FROM logs WHERE ts > '2025-01-01' 触发预读,几百页涌进来只访问一次就淘汰。
- 看
SHOW ENGINE INNODB STATUS\G中Pages read ahead是否持续 > 1000/秒 - 调
innodb_old_blocks_pct:OLTP保持37;混合负载(白天点查+夜间报表)建议25~30;BI直连可压到15~20,但必须同步调高innodb_old_blocks_time到2000~3000 - 绝对不要设
innodb_old_blocks_pct = 5或95——前者让预读失效严重,后者等于放弃分代保护
宽索引才是隐形杀手
一个 INDEX (a,b,c,d,e) 被查询只用前两列,InnoDB 还是得把整页索引数据读进来;回表时再拉一次聚簇索引——两轮IO,缓存里塞满低频页,data_pages / total_pages 接近1.0但命中率上不去。
- 用
EXPLAIN FORMAT=JSON看used_columns和key_parts是否严重不匹配 - 查
performance_schema.table_io_waits_summary_by_table找COUNT_READ异常高的表,再比对information_schema.STATISTICS是否建了5列以上二级索引 - 删或裁:优先用
sys.schema_unused_indexes筛出90天内SELECTS = 0的索引直接 DROP;仍有查询的,按高频 WHERE 字段重排并截断,比如原索引(status, created_at, user_id, order_id),95%只用前两个字段,就建(status, created_at)再删旧的
真正难的不是参数怎么调,而是分清“是内存真不够”,还是“数据根本没被有效缓存”。很多团队花几小时调 innodb_buffer_pool_size,却漏看一条慢查询日志里反复出现的 type: ALL。











