mysql 5.7起query_cache已弃用,8.0彻底移除;真实瓶颈在于缺失索引或低效执行计划,应优先通过explain分析key列、优化索引和join条件,而非调整缓存参数。

query_cache 在 MySQL 5.7 中已弃用,别再调它了
MySQL 5.7 默认关闭 query_cache_type,8.0 直接删掉整个模块。如果你还在查 query_cache_size、Qcache_hits 这类状态变量,说明你用的是旧文档或过时运维脚本。真实瓶颈从来不在查询缓存——它只缓存**完全一致的 SELECT 文本 + 同库同表 + 无函数/变量/时间戳**,而复杂查询几乎全带参数、子查询或 NOW(),根本进不了缓存。
常见错误现象:SHOW STATUS LIKE 'Qcache%' 显示 Qcache_hits 极低,但调大 query_cache_size 后性能反而下降,甚至出现锁争用(Waiting for query cache lock)。这是因为写操作要清空所有相关缓存块,高并发更新下,缓存成了拖累。
- 确认版本:运行
SELECT VERSION();,5.7+ 就该忘掉 query_cache - 检查是否真启用:执行
SHOW VARIABLES LIKE 'query_cache%';,若query_cache_type是 OFF 或 0,别白费劲 - 替代方案不是“换缓存”,而是让查询本身快到不需要缓存——靠索引和执行计划
EXPLAIN 的 key 列为空?说明没走索引
复杂查询慢,第一反应不该是加缓存,而是看 EXPLAIN 输出里 key 列是不是 NULL。只要它是空的,哪怕表只有十万行,JOIN 多一层、WHERE 条件多一个字段,就可能触发全表扫描——这才是缓存命中率低的根因:查询太慢,压根跑不完,缓存没机会生效。
典型场景:多表 JOIN + 时间范围过滤 + GROUP BY。比如 SELECT u.name, COUNT(o.id) FROM users u JOIN orders o ON u.id = o.user_id WHERE o.created_at > '2024-01-01' GROUP BY u.id;,如果 orders.created_at 没索引,或联合索引顺序不对,EXPLAIN 就会显示 type: ALL 和 key: NULL。
- 优先覆盖 WHERE 条件字段:如
WHERE status = ? AND created_at > ?,建索引应为(status, created_at),而非反过来 - JOIN 字段必须类型一致且有索引:
users.id是BIGINT UNSIGNED,orders.user_id却是INT,即使有索引也用不上 - 避免在索引列上用函数:
WHERE DATE(created_at) = '2024-01-01'会让索引失效,改用created_at >= '2024-01-01' AND created_at
ORDER BY + LIMIT 在复杂查询中容易触发 filesort
带排序分页的复杂查询,比如 SELECT ... FROM a JOIN b ON ... WHERE ... ORDER BY b.updated_at DESC LIMIT 20,即使 b.updated_at 有索引,也可能出现 Extra: Using filesort。原因在于 MySQL 无法在 JOIN 后直接利用索引排序——它得先完成连接、过滤,再对结果集排序,数据量一大就磁盘临时表。
解决思路不是加索引数量,而是让排序字段落在驱动表(即 EXPLAIN 中 id 最小的那张表)上,并确保该字段在 WHERE 条件中也被约束。例如把 ORDER BY b.updated_at 换成 ORDER BY a.created_at,同时确保 a 是驱动表且 a.created_at 有索引+被 WHERE 用到。
- 用
STRAIGHT_JOIN强制驱动表顺序(慎用,需配合EXPLAIN验证) - 拆分查询:先查主键 ID(轻量),再用
IN回表,避免大结果集排序 - 注意索引区分度:
INDEX(status)对只有 2–3 个值的字段意义不大,不如换成(status, updated_at)覆盖查询
复合索引的字段顺序比数量更重要
很多人建一堆单列索引,指望 MySQL 自动组合,但优化器只选一个索引(key_len 最长的那个)。真正起效的是复合索引,而字段顺序错了,等于白建。判断依据很简单:WHERE 条件中等值查询字段放前,范围查询字段放后,排序字段尽量靠前或单独补一个。
例如查询 WHERE category = ? AND price BETWEEN ? AND ? ORDER BY sales DESC,最优索引是 (category, price, sales)。如果写成 (category, sales, price),price 的范围条件会让 sales 索引部分失效;如果漏掉 category,那这个索引大概率不会被选中。
- 等值条件(=、IN)可乱序,但建议按区分度从高到低排
- 范围条件(>、
- 用
SHOW INDEX FROM table_name看Seq_in_index,确认字段物理顺序
最常被忽略的一点:索引不是建完就一劳永逸。当 WHERE 条件动态拼接(比如前端传来的多选筛选)、或业务逻辑变更导致查询模式偏移,原有索引可能迅速退化。定期用 slow_query_log 抓出执行时间 > 1s 且 Rows_examined 远大于 Rows_sent 的语句,重新 EXPLAIN,比调任何缓存参数都管用。











