mysql 5.7 查询缓存实际不可用,真正有效的是禁用它并优化 innodb_buffer_pool_size;判断是否生效需同时满足:show variables like 'query_cache_type' 返回 on 且 show status like 'qcache%' 中 qcache_hits 显著增长,否则即为失效或已废弃。

MySQL 5.7 的查询缓存(Query Cache)在实际生产中几乎无法靠配置“提高命中率”——它天生不适合现代应用,盲目调参反而拖慢性能。真正有效的做法是:确认是否真在用 5.7(非 5.7.20+),关掉它,转而优化 innodb_buffer_pool_size。
怎么判断 query_cache_type=1 真的生效了?
别信配置文件写了就起作用。MySQL 5.7.20 起默认禁用,5.7.33 起完全废弃。执行以下命令才能确认:
SHOW VARIABLES LIKE 'query_cache_type';
返回值必须是 ON(即 1)才算启用;若返回 OFF 或 0,说明版本已忽略该配置。再查状态:
SHOW STATUS LIKE 'Qcache%';
如果结果为空或全是 0,不是你配错了,是 MySQL 已经不加载这部分逻辑了。
- 常见假象:my.cnf 里写了
query_cache_type = 1,但SELECT @@query_cache_type返回0 - 根本原因:5.7.33+ 源码里删了 Query Cache 实现,
query_cache_type变成只读变量 - 降级到 5.6 也未必有用:InnoDB 表每秒几条 UPDATE 就能让
Qcache_lowmem_prunes暴涨,命中率长期低于 5%
为什么 query_cache_size 调大反而更慢?
哪怕你确实在用 5.6 或早期 5.7,盲目增大 query_cache_size 会触发三重开销:
- 全局互斥锁
Qcache_lock:所有写入、缓存清理、甚至部分 SELECT 都要排队抢这把锁,高并发下直接卡死 - 内存碎片恶化:当
query_cache_size > 256MB时,Qcache_free_blocks数量飙升,有效利用率断崖下跌 - 无效缓存挤压:
query_cache_limit默认仅1M,大于此值的查询根本进不了缓存,调大query_cache_size前必须同步改它
典型症状:Qcache_lowmem_prunes 每分钟增长数百次,Qcache_hits 却几乎不动——缓存刚存进去就被踢掉。
哪些 SQL 根本不会进缓存?
Query Cache 的“完全匹配”规则极苛刻,日常开发几乎无法维持:
- SQL 字符串必须一字不差:空格、换行、注释、大小写全算在内;ORM 拼出的语句稍有顺序变化就 miss
- 含动态函数的查询直接排除:
NOW()、RAND()、CURRENT_DATE()、USER()等 - 涉及临时表、
LOCK IN SHARE MODE、FOR UPDATE、用户变量或存储函数的查询一律不缓存 - 只要对某张表执行过
INSERT/UPDATE/DELETE,这张表所有缓存瞬间清空——不是按行,是整表
这意味着:哪怕你缓存了一条 SELECT COUNT(*) FROM config,只要业务代码里顺手 INSERT INTO log 一次,这条缓存就没了。
替代方案比调 query_cache 更直接有效
与其花时间调试已淘汰的机制,不如把精力投向真正可控的层:
- 应用层加 Redis:缓存完整结果集,自己控制 key 和失效逻辑,比如用
user:1001:profile代替反复查SELECT * FROM users WHERE id = 1001 - 启动时预热内存:对字典表、配置表,在应用初始化阶段全量加载进本地缓存(如 Guava Cache、Caffeine)
- 调大
innodb_buffer_pool_size:设为物理内存的 70–80%,让热数据常驻内存——这是 InnoDB 层最稳定、最底层的“缓存” - 预生成报表:用
SELECT ... INTO OUTFILE定时导出静态页面,前端直读文件,彻底绕过 DB 查询
最后提醒一句:Query Cache 的失效不是“偶尔漏掉”,而是“一写全崩”。它的设计假设是“极少更新的只读库”,这和今天绝大多数业务场景根本冲突——这点最容易被忽略,却是它在生产环境必然失效的核心原因。











