mysql中SQL执行过程中缓存命中率与性能分析

来源:这里教程网 时间:2026-02-28 20:47:16 作者:

MySQL 查询缓存(Query Cache)已弃用,别再依赖它做性能优化

MySQL 8.0 已彻底移除

query_cache_type
和相关参数,5.7 是最后一个支持它的稳定版本。如果你还在查
Qcache_hits
Qcache_inserts
这类状态变量,说明你可能正踩在一个过时的优化思路上——缓存命中率 ≠ 实际性能收益,尤其在写多读少或表频繁变更的场景下,查询缓存反而会成为锁争用瓶颈。

真正影响 SQL 执行性能的缓存层是 InnoDB Buffer Pool

现代 MySQL 性能分析应聚焦

innodb_buffer_pool_reads
(物理磁盘读)与
innodb_buffer_pool_read_requests
(逻辑读请求)的比值,这才是衡量「热数据缓存效果」的关键指标:

SELECT 
  ROUND((1 - (ibp_reads / ibp_read_requests)) * 100, 2) AS buffer_pool_hit_rate
FROM (
  SELECT 
    VARIABLE_VALUE AS ibp_reads
  FROM performance_schema.global_status 
  WHERE VARIABLE_NAME = 'Innodb_buffer_pool_reads'
) r
CROSS JOIN (
  SELECT 
    VARIABLE_VALUE AS ibp_read_requests
  FROM performance_schema.global_status 
  WHERE VARIABLE_NAME = 'Innodb_buffer_pool_read_requests'
) req;
Innodb_buffer_pool_reads > 0
表示有磁盘 I/O,需关注是否 buffer pool 太小或查询未走索引
命中率长期低于 95% 通常意味着 buffer pool 不足,或存在大量全表扫描 该指标只反映页级缓存效果,不等同于“SQL 结果被缓存”,和旧版 Query Cache 完全无关

如何判断某条 SQL 是否真的从 Buffer Pool 中受益

单条语句无法直接看到“本次执行是否命中 Buffer Pool”,但可通过

EXPLAIN FORMAT=JSON
+ 执行前后状态差定位真实开销:

先执行
SHOW STATUS LIKE 'Innodb_buffer_pool_read%';
记下
Innodb_buffer_pool_read_requests
Innodb_buffer_pool_reads
运行目标 SQL(确保无其他并发干扰) 再次执行
SHOW STATUS
,观察
Innodb_buffer_pool_reads
是否增加:若增加,说明有页未命中,触发了磁盘读
配合
EXPLAIN FORMAT=JSON
查看
rows_examined
used_columns
,确认是否走了预期索引

注意:

Handler_read_*
系列状态(如
Handler_read_next
)反映的是存储引擎扫描行为,不是缓存命中指标,勿混淆。

performance_schema 中可跟踪的缓存相关事件

想深入分析某类操作的内存/IO 模式,开启以下 instruments 后查

events_waits_history_long

UPDATE performance_schema.setup_instruments 
SET ENABLED = 'YES', TIMED = 'YES' 
WHERE NAME LIKE 'wait/io/file/innodb/%' OR NAME LIKE 'wait/synch/mutex/innodb/%';
重点关注
wait/io/file/innodb/innodb_data_file
—— 表示实际磁盘读写等待
wait/synch/mutex/innodb/buf_pool_mutex
高频出现?说明 buffer pool latch 争用严重,可能是 buffer pool 过大且未启用
innodb_buffer_pool_instances
这些事件不会告诉你“缓存命中”,但能暴露缓存失效或争用的真实代价

Buffer Pool 的大小、实例数、预热策略、以及 SQL 是否能复用已有页帧,远比“缓存命中率”这个数字更能决定性能。盯着一个被废弃的指标,不如先确认你的

innodb_buffer_pool_size
是否设为物理内存的 50%–75%,以及有没有定期用
SELECT * FROM table LIMIT 1
类操作意外刷掉热页。

相关推荐