MySQL 8.0 彻底删除 query_cache 模块,非禁用而是源码级移除;配置 query_cache_type 等变量会导致启动失败报错,必须从配置文件中整行删除所有 query_cache_ 开头的行(含注释),否则服务无法启动。

MySQL 8.0 不是“关掉了”查询缓存,而是从源码里物理删掉了整个 query_cache 模块——你配它,服务就起不来;你查它,会直接报 Unknown system variable 'query_cache_type'。
为什么一启动就报 Unknown system variable 'query_cache_type'
这不是配置写错,是变量名在编译期就被剔除了。MySQL 8.0.3 起,所有 query_cache_type、query_cache_size、query_cache_limit 等符号已从代码中彻底移除。mysqld 解析配置时找不到对应变量定义,直接拒绝加载并退出。
- 别注释,别设成 0,别加引号——必须从
my.cnf(或my.ini)里整行删除所有以query_cache_开头的配置,包括被#注释掉的 - Docker 镜像、RDS 参数组、Percona Server 8.x、MariaDB 10.6+ 同样不识别,复用旧模板必卡在这一步
- 验证方式:升级前先跑
mysqld --validate-config,比等日志报错更早发现问题
为什么 UPDATE 一行就清空整张表所有缓存
Query Cache 的失效粒度是表级(table-level),且不感知语句影响范围。一次 UPDATE users SET status = 'paid' WHERE id = 123,会立刻触发以下动作:
- 扫描所有缓存条目,把所有 SQL 文本中含
FROM users或JOIN users的结果全标记为失效 - 持住全局锁
LOCK_query_cache执行清空,期间所有新 SELECT 都得排队等待 - 事务尚未提交,缓存已失效;但新查询又不能读未提交数据,形成“失效了却还不能用”的真空期
- 分区表默认禁用 Query Cache,而生产环境大表基本都分区——这说明它的设计早已脱离主流用法
为什么高并发下它反而拖慢 QPS
核心瓶颈是那把无法分片、不可降级的全局锁 LOCK_query_cache。所有 SELECT 检查缓存、所有 DML 清空缓存,都必须串行抢它。
- 几十个并发 SELECT 同时到达,不是并行响应,而是在锁上排队——CPU 花在上下文切换,而不是执行 SQL
- 哪怕只查一个字段、带 LIMIT 1,也得等锁;而 InnoDB Buffer Pool 的页读是无锁并发的
- Percona 实测:200+ QPS 写入场景下,开启 Query Cache 的吞吐比关闭时低 15%~30%
-
SHOW PROCESSLIST里大量出现Waiting for query cache lock,就是典型征兆
为什么 ORM 场景下几乎零命中
Query Cache 要求 SQL 字节级完全一致,而现代应用层几乎不可能满足这个条件:
- ORM(如 Django、MyBatis、Sequelize)生成的 SQL 天然带随机空格、换行、注释,
SELECT * FROM users WHERE id = 1和SELECT * FROM users WHERE id=1就是两个 key -
PREPARE/EXECUTE预处理语句根本绕过 Query Cache 路径,不参与任何缓存逻辑 - 含
NOW()、RAND()、用户变量、子查询、视图的查询,一律被跳过 - 缓存命中率常年低于 0.1%,但内存碎片整理、哈希计算、全局查找这些 CPU 开销照常发生
真正该花时间调的不是 query_cache_size,而是 innodb_buffer_pool_size 是否合理、慢查询是否漏了索引、连接池是否在反复建连。Query Cache 的幽灵还在不少配置模板里残留——删掉它,系统反而更轻、更稳、更容易诊断。


















