MySQL 8 查询缓存已废除详解:从架构、历史到替代方案
结论先行 :MySQL 8.0 已彻底移除 Query Cache(查询缓存)功能 。
若你在 5.7 或更早版本中见过 "命中查询缓存直接返回结果",那在 8.0 之后已不复存在。
本文将系统讲解:为什么移除、怎么验证、有哪些替代方案。
🧾 一、官方确认:Query Cache 在 MySQL 8.0 中被移除
在 MySQL 官方文档中明确说明:
The Query Cache has been removed from MySQL 8.0.
✅ 实验验证
你可以在终端中执行:
sql
SHOW VARIABLES LIKE 'query_cache%';
- MySQL 5.7 输出示例:
Variable_name | Value |
---|---|
query_cache_size | 1048576 |
query_cache_type | OFF |
query_cache_limit | 1048576 |
- MySQL 8.0 输出示例:
结果为空(或报错:Unknown system variable 'query_cache_size'
)。
⚙️ 参数对比表
参数名 | MySQL 5.7 | MySQL 8.0 | 说明 |
---|---|---|---|
query_cache_size |
✅ 可配置缓存大小 | ❌ 已删除 | |
query_cache_type |
✅ 可设置 ON/OFF/DEMAND | ❌ 已删除 | |
query_cache_limit |
✅ 可配置单条结果上限 | ❌ 已删除 | |
Qcache_hits 等状态变量 |
✅ 存在 | ❌ 已删除 |
💡 二、为什么 MySQL 要移除 Query Cache?
-
严重的全局锁争用
Query Cache 是一个全局共享内存区,任何写操作(INSERT/UPDATE/DELETE/DDL)都会锁住整个缓存区。
在高并发下反而导致性能下降。
-
失效粒度粗
任意表的修改都会使所有涉及该表的缓存失效。
对更新频繁的表几乎命中率为零。
-
内存碎片严重,维护复杂
大小不一的结果集插入会产生内存碎片,清理和整理耗时高。
👉 因此,MySQL 官方团队认为 "成本 > 收益",在 8.0 中彻底移除。
🧩 三、MySQL 查询执行架构概览
3.1 旧版(≤5.7)带 Query Cache 的逻辑
flowchart LR
A[客户端] --> B[连接器]
B --> C{查询缓存?}
C -- 命中 --> R[直接返回结果]
C -- 未命中 --> D[解析器/词法语法]
D --> E[优化器/生成执行计划]
E --> F[执行器/调用存储引擎]
F --> P[(InnoDB 数据页/索引页)]
F -->|结果| G[返回客户端]
R --> G
3.2 MySQL 8.0 的真实执行链路(已无 Query Cache)
flowchart LR
A[客户端] --> B[连接器]
B --> D[解析器/词法语法]
D --> E[优化器/生成执行计划]
E --> F[执行器/调用存储引擎]
F --> P[(InnoDB Buffer Pool
页缓存/自适应哈希)] F --> G[返回客户端]
页缓存/自适应哈希)] F --> G[返回客户端]
注:图中的 Buffer Pool 不是"查询结果缓存",它缓存的是数据页 与索引页。
🧠 四、MySQL 5.7 的 Query Cache 工作原理(用于对比理解)
4.1 存储结构
- Key:SQL 原文 + 当前数据库上下文。
- Value:完整结果集。
- 全局共享内存区:通过全局锁管理,容易碎片化。
4.2 执行流程
sequenceDiagram
participant C as Client
participant S as MySQL Server (<=5.7)
C->>S: 发送 SQL
S->>S: 检查 Query Cache
alt 命中
S-->>C: 直接返回结果集
else 未命中
S->>S: 解析/优化/执行
S->>S: 将结果写入缓存
S-->>C: 返回结果
end
4.3 缓存失效机制
- 任意表的更新操作都会使与该表有关的缓存条目全部失效。
- 写多读少的表几乎无法命中缓存。
🔍 五、MySQL 8.0 中仍存在的"其他缓存机制"
类型 | 缓存内容 | 是否等价于 Query Cache |
---|---|---|
InnoDB Buffer Pool | 数据页 / 索引页 | ❌ 不是结果缓存 |
Prepared Statement Plan Cache | 执行计划 | ❌ 不缓存结果 |
Table Cache | 已打开的表句柄 | ❌ |
Metadata Cache | 数据字典元信息 | ❌ |
这些机制主要提升 I/O 与解析效率,并非存储"SQL 查询结果"。
🧮 六、如何在 MySQL 8.0 实现"查询缓存"效果
6.1 Cache-Aside 模式(推荐)
flowchart LR
A[客户端] --> B[应用服务]
B -->|查| C{Redis/Memcached}
C -- 命中 --> R[直接返回结果]
C -- 未命中 --> D[MySQL 8.0]
D --> E[(InnoDB)]
D -->|结果写入| C
D --> R
6.2 缓存 Key 设计建议
- 使用唯一主键或 SQL 指纹。
- 可携带版本号:
user:123:v27
。 - 大结果集可分页存储。
6.3 缓存失效策略
- TTL + 惰性删除:简单易行;
- 精确失效:监听 Binlog / CDC(如 Debezium, Canal)更新缓存。
6.4 缓存伪代码
pseudo
func getUserProfile(uid):
key = "user:%d:v%d".format(uid, getVersion(uid))
if cache.exists(key):
return cache.get(key)
row = mysql.query("SELECT * FROM user WHERE id=?", uid)
cache.setex(key, ttl=600, value=row)
return row
⚙️ 七、多级缓存执行链路
sequenceDiagram
participant U as User
participant A as 应用(L1本地缓存)
participant R as Redis(L2)
participant M as MySQL 8.0
U->>A: 查询 Q
alt L1 命中
A-->>U: 返回
else L1 未命中
A->>R: GET key(Q)
alt L2 命中
R-->>A: value
A-->>U: 返回并写入L1
else L2 未命中
A->>M: 执行 Q
M-->>A: 结果集
A->>R: SETEX key,value,ttl
A-->>U: 返回并写入L1
end
end
📈 八、性能观测与调优建议
- Buffer Pool 命中率 :
SHOW ENGINE INNODB STATUS
。 - 慢查询分析 :
EXPLAIN ANALYZE
。 - 表缓存参数 :
table_open_cache
、thread_cache_size
。 - 应用层指标:缓存命中率、穿透、击穿、雪崩监控。
🧭 九、总结
结论 | 说明 |
---|---|
✅ MySQL 8.0 移除查询缓存 | 所有相关参数与代码被删除 |
🔍 想要缓存结果 | 应在应用层或代理层实现 |
⚙️ 推荐架构 | L1 本地缓存 + L2 Redis + MySQL |
🚫 不再建议 | 使用数据库级"结果缓存" |
一句话总结: 在 MySQL 8.0 里,"查询缓存"已成历史,但缓存思想永不过时。
真正的高性能来自于:架构分层 + 精确失效 + 多级缓存。