MySQL 8 查询缓存已废除详解:从架构、历史到替代方案

264 阅读3分钟

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.
— 来源:MySQL 8.0 Reference Manual - Query Cache

✅ 实验验证

你可以在终端中执行:

SHOW VARIABLES LIKE 'query_cache%';
  • MySQL 5.7 输出示例:
Variable_nameValue
query_cache_size1048576
query_cache_typeOFF
query_cache_limit1048576
  • MySQL 8.0 输出示例:

结果为空(或报错:Unknown system variable 'query_cache_size')。

⚙️ 参数对比表

参数名MySQL 5.7MySQL 8.0说明
query_cache_size✅ 可配置缓存大小❌ 已删除
query_cache_type✅ 可设置 ON/OFF/DEMAND❌ 已删除
query_cache_limit✅ 可配置单条结果上限❌ 已删除
Qcache_hits 等状态变量✅ 存在❌ 已删除

💡 二、为什么 MySQL 要移除 Query Cache?

  1. 严重的全局锁争用
    Query Cache 是一个全局共享内存区,任何写操作(INSERT/UPDATE/DELETE/DDL)都会锁住整个缓存区。
    在高并发下反而导致性能下降。

  2. 失效粒度粗
    任意表的修改都会使所有涉及该表的缓存失效。
    对更新频繁的表几乎命中率为零。

  3. 内存碎片严重,维护复杂
    大小不一的结果集插入会产生内存碎片,清理和整理耗时高。

👉 因此,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<br/>页缓存/自适应哈希)]
    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 缓存伪代码

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_cachethread_cache_size
  • 应用层指标:缓存命中率、穿透、击穿、雪崩监控。

🧭 九、总结

结论说明
✅ MySQL 8.0 移除查询缓存所有相关参数与代码被删除
🔍 想要缓存结果应在应用层或代理层实现
⚙️ 推荐架构L1 本地缓存 + L2 Redis + MySQL
🚫 不再建议使用数据库级“结果缓存”

一句话总结: 在 MySQL 8.0 里,“查询缓存”已成历史,但缓存思想永不过时。
真正的高性能来自于:架构分层 + 精确失效 + 多级缓存