MySQL 是否缓存查询结果是许多刚接触数据库性能调优的人会产生的疑问。从早期版本的设计来看,MySQL 确实提供过一种名为查询缓存(Query Cache)的内置机制,但它和我们日常理解的通用结果缓存并不完全相同,而且在高版本中已经被官方移除。理解它的工作原理与局限,有助于避免在架构设计上踩坑。

一、MySQL 查询缓存是什么
查询缓存是 MySQL 服务端在内存中维护的一块区域,用来存储客户端发送的 SELECT 语句文本以及对应的完整结果集。当新的查询到达时,MySQL 会先计算该 SQL 语句的哈希值,若能在缓存中找到完全相同的语句(包括空格、大小写、数据库名等都必须一致),就直接返回缓存结果,跳过解析、优化和执行阶段。
这种机制听起来能显著提升性能,但实际上约束非常严格。只要被查询的表中发生了任何数据修改(INSERT、UPDATE、DELETE、TRUNCATE 等)或者表结构变更(ALTER TABLE),所有涉及该表的缓存条目都会立即失效并被清除。因此在写操作频繁的系统中,查询缓存的命中率往往低得可怜,反而因为频繁的失效检查和锁竞争拖慢整体吞吐。
二、查询缓存的配置与验证
在 MySQL 5.6 及之前的版本中,可以通过系统变量来开启和调节查询缓存。核心参数包括 query_cache_type 和 query_cache_size。下面的示例展示了如何查看当前配置以及手动启用缓存:
-- 查看查询缓存相关配置 SHOW VARIABLES LIKE 'query_cache%'; -- 临时开启查询缓存(仅对当前会话生效需设为 DEMAND) SET GLOBAL query_cache_type = ON; SET GLOBAL query_cache_size = 67108864; -- 分配 64MB 缓存 -- 执行一条会被缓存的查询 SELECT SQL_CACHE * FROM user WHERE id = 1; -- 查看缓存命中情况 SHOW STATUS LIKE 'Qcache%';
通过 Qcache_hits 和 Qcache_inserts 的比值,可以粗略判断缓存是否有效。如果 Qcache_not_cached 数值很大,说明大量查询因为不符合缓存条件(例如使用了用户变量、临时表、不确定函数如 NOW())而被直接跳过。
需要注意的是,SQL_CACHE 和 SQL_NO_CACHE 提示符只有在 query_cache_type 设为 DEMAND 时才具有强制意义。当类型设为 ON 时,所有可缓存查询都会默认缓存,除非显式写上 SQL_NO_CACHE。这种细粒度控制在实际业务中很少被正确使用,多数团队索性关闭该功能。
三、为什么官方放弃了查询缓存
从 MySQL 5.7 开始,官方文档已将查询缓存标记为废弃(deprecated),到 MySQL 8.0 则彻底删除该特性。根本原因在于它的设计无法适应现代高并发场景。查询缓存使用单一互斥锁保护整个缓存区,在多核服务器上,大量线程争用同一把锁会成为严重瓶颈。
此外,缓存失效的粒度太粗。任何一条针对某表的写入都会令该表所有查询缓存失效,这意味着即便只是更新一行数据,相关表上成千上万条不同条件的 SELECT 缓存都会瞬间清空。在读写混合的业务里,缓存几乎刚写入就被淘汰,资源投入产出比极低。官方也建议用户改用应用层缓存(如 Redis)或重点优化 InnoDB 缓冲池。
| 版本 | 查询缓存状态 | 建议 |
|---|---|---|
| MySQL 5.6 及以前 | 默认开启可选 | 写少读多且 SQL 固定时可谨慎启用 |
| MySQL 5.7 | 废弃 | 逐步关闭,避免依赖 |
| MySQL 8.0+ | 已移除 | 使用 Redis 或优化索引与缓冲池 |
四、替代方案与正确优化思路
如果业务确实需要缓存查询结果,更合理的做法是在应用端引入分布式缓存组件。例如使用 Redis 以查询特征(如方法名加参数哈希)作为 key 存储序列化结果,并借助消息队列在写操作时精准淘汰对应 key,这样失效粒度可以控制到具体业务对象。
对于数据库自身,应当把精力放在 InnoDB 的 buffer pool 上。buffer pool 缓存的是数据页和索引页,不属于 SQL 结果缓存,但能大幅减少磁盘 IO。配合合理的复合索引、覆盖索引以及避免 SELECT * 的写法,往往比查询缓存带来更稳定的性能提升。下方代码展示了如何通过 EXPLAIN 检查语句是否命中索引:
-- 分析查询执行计划 EXPLAIN SELECT id, name FROM user WHERE status = 1 AND create_time > '2023-01-01'; -- 若 type 为 ALL 表示全表扫描,需考虑建立索引 -- 建立联合索引示例 CREATE INDEX idx_status_ctime ON user(status, create_time);
在绝大多数生产环境中,关闭 MySQL 查询缓存不仅不会降低性能,反而能减少锁等待和内存碎片。理解这一点,我们就能更清醒地看待数据库层的缓存能力边界,把架构设计建立在可扩展、可观测的基础之上。
五、常见误区总结
不少人以为只要数据库开着查询缓存,重复页面请求背后的相同 SQL 就一定会变快,这是典型误解。因为缓存要求 SQL 文本字节级一致,而很多 ORM 框架会自动拼接空格、注释或排序参数,导致哈希不匹配。再加上写操作清缓存的特性,实际命中率可能趋近于零。
另一个误区是认为查询缓存能替代 Redis。二者层级不同:查询缓存位于数据库内部、随实例重启消失且受限单机内存;Redis 等外部缓存可跨服务共享、支持复杂淘汰策略与集群扩展。明确职责分工,才能让系统既稳健又高效。
MySQL查询缓存query_cache修改时间:2026-08-07 09:15:28