必须删除配置文件中所有以query_cache_开头的行(含注释),因mysql 8.0.3起已物理移除query cache模块,变量彻底不存在;否则启动报错,且该锁机制在高并发下导致严重性能瓶颈。

MySQL 8.0 启动报 Unknown system variable 'query_cache_type' 怎么办
这不是配置写错了,是变量本身已不存在。MySQL 8.0.3 起,整个 Query Cache 模块被物理移除,解析器在编译期就删掉了所有 query_cache_* 符号。哪怕配置文件里只有一行 # query_cache_type = 0(注释状态),某些版本的配置解析器仍会尝试识别并失败。
必须手动删掉 my.cnf 或 my.ini 中所有以 query_cache_ 开头的行——包括注释、空格、换行符前后的残留。Percona Server 8.x、MariaDB 10.6+ 同样不识别,云数据库(如 RDS)或 Docker 镜像若复用旧参数模板,也会卡在这一步。
验证是否清干净:
- 启动 mysqld,看错误日志末尾是否还有 Unknown system variable 'query_cache_type'
- 连上后执行 SELECT * FROM performance_schema.variables_info WHERE VARIABLE_NAME LIKE 'query_cache%';,结果应为空
为什么高并发下 SELECT 总卡在 Waiting for query cache lock
Query Cache 的底层锁叫 LOCK_query_cache,是个单点全局互斥锁。所有操作——查命中、存结果、清失效条目——都得抢它。哪怕只是判断 SELECT * FROM users WHERE id = 123 在不在缓存里,也要排队等锁。
这个锁无法分片、不能降级、不区分读写。OLTP 场景下几十个并发 SELECT 线程集中阻塞,CPU %system 异常升高,SHOW PROCESSLIST 里大量线程显示 Waiting for query cache lock 就是典型征兆。
更糟的是:即使你设了 query_cache_size = 0,这部分锁判断逻辑依然存在,锁照样要抢——开销没少,收益为零。
为什么 UPDATE 一行就让整张表缓存全失效
Query Cache 的失效粒度是表级(table-level),不是语句级或行级。执行 UPDATE users SET name='x' WHERE id=1,所有命中 users 表的缓存条目——不管 SQL 多具体、是否带 LIMIT、是否只查一个字段——全部被立即清除。
清空动作本身还要持 LOCK_query_cache,期间新查询既不能读未提交数据,又拿不到缓存结果,形成“失效了却还不能用”的真空。事务未提交时缓存已失效,但隔离级别又不允许读脏数据,逻辑上就卡住了。
分区表默认禁用 Query Cache,而生产环境大表基本都分区,说明该机制早已脱离主流用法。
为什么缓存命中率常年低于 0.1 却还在吃资源
命中条件苛刻到近乎不可用:
- SQL 必须字节级完全一致:SELECT id FROM t WHERE x=1 和 SELECT id FROM t WHERE x = 1(空格差一点)就是两个 key
- PREPARE/EXECUTE、含 NOW()、RAND()、用户变量、子查询、视图的查询,一律跳过缓存路径
- ORM(如 Django、MyBatis)生成的语句天然带随机空格和注释,基本不命中
内存按 query_cache_min_res_unit(默认 4KB)硬切,缓存结果集仅 1KB 也要占 4KB,碎片严重时 FLUSH QUERY CACHE 自身就要持锁、吃 CPU。实测 CPU usage 升高 13%~18%,而 Qcache_hits / Com_select 比值常年低于 0.1。
真正该盯紧的是 innodb_buffer_pool_size 和应用层缓存策略——Query Cache 不是“缓存没了”,而是把资源腾出来给了更可控、可伸缩的机制。











