MySQL 8.0移除查询缓存,因其全局互斥锁导致高并发下QPS下降15%~30%;表级失效、字节级命中要求及非确定性语句不缓存等缺陷使其在真实负载中拖慢性能。

生产环境不建议开启MySQL查询缓存——它在绝大多数真实负载下会拖慢QPS、抬高延迟,且从MySQL 8.0起已被彻底移除。
query_cache_type=1 为什么会让QPS下降30%?
根本原因在于全局互斥锁(global mutex):每次查询命中/未命中、每次表更新触发失效,都要抢同一把锁。高并发时大量线程卡在锁等待上,CPU空转,响应时间反而拉长。实测中,哪怕缓存命中率有5%,只要写入稍多(比如每秒几十次UPDATE),整体吞吐就明显劣化。
另外几个硬伤:
- 缓存失效是表级的——
UPDATE users SET name='x' WHERE id=1会让所有涉及users表的SELECT缓存瞬间清空 - SQL必须字节级一致才能命中:
SELECT * FROM t和select * from t是两条不同缓存 - 含
NOW()、RAND()、用户变量、子查询等非确定性语句直接不进缓存 -
query_cache_size实际生效需 ≥ 1024 字节且为1024整数倍;设成4000会被MySQL静默修正,导致你以为开了其实没开
MySQL 5.7及更早版本如何安全关闭?
两种方式,优先用永久关闭,避免重启后回弹:
安装、修复、更新或打包一个基于 Python 和 PyMySQL 实现的 Windows `mysql` 命令。适用于用户希望在 PowerShell/CMD 中输入 `mysql` 时重定向到 Python 实现、需要交互式输入 MySQL 连接信息、需要同步到当前用户本地 bin 目录,或需要将该能力封装成可公开复用的 Codex skill。
- 临时关闭(当前会话不生效,仅影响后续新连接):
SET GLOBAL query_cache_type = 0;SET GLOBAL query_cache_size = 0; - 永久关闭:编辑
my.cnf的[mysqld]段,添加两行:query_cache_type = 0query_cache_size = 0
改完必须systemctl restart mysql或service mysqld restart - 验证是否生效:
SHOW VARIABLES LIKE 'query_cache%';→ 所有值应为0或OFFSHOW STATUS LIKE 'Qcache%';→Qcache_hits应长期为0
为什么不能只用 SQL_NO_CACHE 应急?
SELECT SQL_NO_CACHE ... 看似灵活,但问题很实在:
- 它只跳过单条语句的缓存写入,不阻止其他语句往缓存里塞数据,内存和锁开销照旧
- 业务代码里漏加一个
SQL_NO_CACHE,就可能让缓存被污染,进而拖累其他查询 - ORM(如MyBatis、Django ORM)通常不暴露原生SQL控制点,很难统一加这个hint
- 它无法规避缓存失效风暴——只要有人对表做DML,你那些没加hint的查询依然会触发全表缓存清空
真正该做的,是把缓存逻辑下沉到应用层:用 Redis 缓存热点结果、用客户端本地缓存减少重复请求、或借助 ProxySQL 做语句级路由与缓存。MySQL自身的query_cache,现在只剩一个价值:帮你理解“为什么缓存不一定等于快”。










