mysql查询缓存未真正关闭需彻底删除所有query_cache_*配置并重启;确认需show variables和status双验证,qcache_lowmem_prunes高频上涨表明全局锁争抢严重,应禁用而非调优。

查 query_cache_type 是否真关了
很多人以为 SET GLOBAL query_cache_type = 0 就完事了,其实这只是会话级临时关闭,MySQL 5.7 启动时会“自作主张”:只要配置文件里还留着 query_cache_size = 1048576 这类行,它就会把 query_cache_type 拉回 1。必须删光所有 query_cache_* 配置项(包括 query_cache_limit、query_cache_min_res_unit),再重启 mysqld。
确认生效的唯一方式是执行:
SHOW VARIABLES LIKE 'query_cache%';
全部返回 OFF 或 0;再跑一次:
SHOW STATUS LIKE 'Qcache%';
确保 Qcache_hits 不再增长——否则说明锁还在抢,阻塞没解除。
看 Qcache_lowmem_prunes 是否高频上涨
这个值每分钟涨几百甚至上千,基本坐实查询缓存正在制造假性瓶颈。它代表缓存刚写入就被踢出,背后是频繁的表级失效+全局锁竞争。此时哪怕 Qcache_hits 看似有几十次,命中率算下来也常低于 5%,属于“白忙活还占资源”。
关键点在于:Qcache_lowmem_prunes 上涨 ≠ 缓存不够大,而是写操作太密、锁太重。InnoDB 表只要每秒有几十次 DML,缓存就形同虚设。
- 不要调
query_cache_size,它只会让锁争抢更久 - 别信“我只读不写”,主从同步线程(
SQL_THREAD)也会触发表级清空 -
SQL_NO_CACHE语句仍要走哈希计算+锁检查,开销照旧
用 perf top 确认 query_cache_lock 是否卡住线程
在 Linux 上直接运行:
perf top -p $(pgrep mysqld)
如果高频看到 query_cache_lock 符号,说明大量线程正卡在等待这把全局互斥锁——这就是阻塞的物理证据。不是慢查询本身慢,是大家排队等同一把锁。
这种现象在高并发 SELECT 场景下特别明显,哪怕你只读一张小表,只要其他线程正在写日志表或计数器表,你的查询就得等。
- 锁粒度是全局的,不是按表、按库、甚至不是按语句,是整个模块一把锁
- 即使
Com_select很高,Threads_running却压不下去,大概率就是它 - 关掉查询缓存后,
Threads_running峰值下降、P95 响应时间收窄,才是真实收益
别把问题错判成慢查询或索引缺失
当响应变慢时,第一反应常是 EXPLAIN 慢 SQL 或加索引,但若根本原因是 query_cache_lock,这些操作全无效。因为瓶颈不在执行路径,而在执行前的锁等待阶段。
典型误判信号:
- EXPLAIN 显示走索引、type 是 ref,但实际执行耗时波动极大
- 慢查询日志里没几条记录,但监控显示平均延迟持续偏高
- KILL 掉几个长事务后,整体延迟没改善,但
Qcache_lowmem_prunes仍在狂涨
真正该调的是 innodb_buffer_pool_size 和索引设计,不是 query_cache_size。查询缓存的残余配置最容易被忽略,也最可能在你毫无察觉时悄悄拖垮系统。











