MySQL 查询缓存:机制、优缺点与最佳实践
MySQL 的查询缓存(Query Cache)是一项旨在通过缓存 SELECT 语句的结果集来提升查询性能的机制。然而,它的适用场景有限,且在 MySQL 8.0 中已被移除。以下详细解析其工作原理、失效机制及使用建议。
查询缓存的工作原理
查询缓存的核心逻辑是缓存 SELECT 语句的结果集,当相同查询再次执行时直接返回缓存结果,跳过 SQL 解析、执行计划生成和实际执行步骤。

- 缓存触发流程:
- 执行
SELECT语句时,MySQL 首先对 SQL 进行大小写敏感的哈希计算,生成缓存键。 - 检查缓存中是否存在该键对应的结果集:
- 若命中(缓存有效),则验证用户权限后直接返回结果。
- 若未命中,则执行完整查询流程(解析、优化、执行),并将结果存入缓存。
- 执行
- 缓存存储形式:
- 以键值对形式存储,键为 SQL 语句的哈希值,值为查询结果集。
- 缓存空间由
query_cache_size控制,默认分配一块连续内存,按固定大小的块(query_cache_min_res_unit)分配给结果集。
查询缓存的失效机制
查询缓存的最大局限在于极易失效,任何与缓存相关的表发生变更时,关联的所有缓存都会被清空。具体触发条件包括:
- 数据变更:对表执行
INSERT/UPDATE/DELETE/TRUNCATE等操作。 - 结构变更:执行
ALTER TABLE/DROP TABLE等修改表结构的操作。 - 缓存空间不足:当缓存满时,会根据 LRU(最近最少使用)策略淘汰旧缓存(
Qcache_lowmem_prunes计数增加)。
注意:即使表数据未实际变更(如
UPDATE t SET col=col),也会触发缓存失效,这是导致缓存命中率低的常见原因。
查询缓存的核心配置参数
| 参数 | 作用 | 推荐值 |
|---|---|---|
query_cache_type |
控制缓存开关: - OFF:禁用缓存 - ON:启用缓存(默认) - DEMAND:仅缓存带 SQL_CACHE 关键字的查询 |
读写频繁场景设为 OFF 或 DEMAND |
query_cache_size |
缓存总内存大小(必须为 1024 的倍数) | 建议 0(禁用)或根据实际需求设置(如 64M-256M) |
query_cache_limit |
单条查询结果的最大缓存大小(超过则不缓存) | 避免大结果集占用过多缓存(如 1M) |
query_cache_min_res_unit |
缓存块的最小分配单位 | 默认 4KB,大结果集场景可增大(减少碎片) |
query_cache_wlock_invalidate |
MyISAM 表写锁时是否允许读缓存(FALSE 允许,TRUE 等待) |
保持默认 FALSE(减少读阻塞) |
查询缓存的状态监控
通过 SHOW STATUS LIKE 'Qcache%' 查看缓存使用情况,关键指标解析:
| 状态变量 | 含义 | 分析建议 |
|---|---|---|
Qcache_hits |
缓存命中次数 | 数值越高,缓存效果越好 |
Qcache_inserts |
缓存写入次数 | 若 Qcache_hits / Qcache_inserts 比值低(如 < 0.5),缓存性价比低 |
Qcache_lowmem_prunes |
因内存不足淘汰的缓存数 | 频繁增加说明 query_cache_size 过小或碎片过多 |
Qcache_free_blocks |
空闲缓存块数 | 数值过大(如 > Qcache_total_blocks / 10)说明碎片严重,需执行 FLUSH QUERY CACHE 整理 |
Qcache_not_cached |
未被缓存的查询数 | 过大可能因 SQL 包含变量、函数等不支持缓存的内容 |
查询缓存的适用场景与局限性
适用场景:
- 读多写少的静态数据(如配置表、字典表),且查询模式固定(SQL 完全一致)。
- 频繁重复执行的简单查询(如
SELECT count(*) FROM t),结果集小且稳定。
局限性:
- 缓存命中率低:写操作频繁的表会导致缓存频繁失效,甚至缓存维护开销超过收益。
- 不支持动态查询:包含用户变量(
@var)、函数(NOW())、存储过程等的查询无法缓存。 - 内存碎片问题:频繁的缓存写入和淘汰会产生碎片,降低内存利用率。
- MySQL 8.0 已移除:官方认为其维护成本高,且在高并发场景下性能不佳,建议通过应用层缓存(如 Redis)替代。
最佳实践
禁用查询缓存:
- 对于读写频繁的业务(如电商、社交),直接设置
query_cache_type = OFF和query_cache_size = 0,避免缓存维护开销。
- 对于读写频繁的业务(如电商、社交),直接设置
按需启用缓存:
若需保留缓存,将
query_cache_type设为DEMAND,仅对特定查询手动开启缓存:1
2
3
4
5-- 强制缓存该查询
SELECT SQL_CACHE id, name FROM static_table WHERE type = 1;
-- 强制不缓存该查询
SELECT SQL_NO_CACHE * FROM frequently_updated_table;
替代方案:
- 用应用层缓存(Redis、Memcached)缓存查询结果,灵活性更高且不受表变更影响。
- 优化索引和查询语句,减少重复查询的执行时间