查询缓存为何 8.0 移除

查询缓存为何 8.0 移除

查询缓存是什么

查询缓存(Query Cache)是 MySQL 在 Server 层的一个优化特性:将 SELECT 语句的完整结果缓存起来,相同的查询直接返回缓存结果,跳过解析、优化和执行。

graph TD
    A[SELECT * FROM user WHERE id=1] --> B{查询缓存中<br/>是否存在?}
    B -->|命中 | C[直接返回缓存结果]
    B -->|未命中 | D[正常执行流程]
    D --> E[将结果写入缓存]
    E --> F[返回结果]

看似美好,实则鸡肋

问题一:缓存失效太频繁

-- 第一次查询,缓存建好了
SELECT * FROM product WHERE id = 1;  -- 缓存命中

-- 任何更新操作都会清空整个表的缓存!
UPDATE product SET price = 99 WHERE id = 1;  -- ❌ 整表缓存失效

-- 下一个查询又要重新查
SELECT * FROM product WHERE id = 1;  -- ❌ 缓存未命中,重新执行
graph LR
    A[写入缓存] --> B[任何UPDATE/INSERT/DELETE]
    B --> C[缓存失效]
    C --> D[重新查询]
    D --> A
    E[循环:对写频繁的表<br/>缓存几乎永远不生效]

问题二:缓存粒度太粗

-- 以下两条完全不同,无法复用缓存
SELECT * FROM user WHERE id = 1;  -- 缓存key: select * from user where id = 1
SELECT * FROM user WHERE id = 2;  -- 缓存key: select * from user where id = 2

-- 连空格不同都算不同查询
SELECT * FROM user WHERE id =1;   -- 缓存key不同!

查询缓存基于精确字符串匹配,任何字符差异都导致缓存 miss。

问题三:缓存管理有开销

sequenceDiagram
    participant Q as 查询
    participant QC as 查询缓存
    participant T as 表数据

    Q->>QC: 检查缓存(加锁)
    QC-->>Q: 命中/未命中
    QC-->>QC:  缓存查找本身有锁开销

    T->>QC: UPDATE 清空缓存
    Note over QC:  清空操作也需要排他锁<br/>高并发下锁竞争严重

缓存互斥:在检查缓存时,MySQL 需要加锁,这在高并发下是一个严重的性能瓶颈。

问题四:对写密集型场景是灾难

场景 查询缓存效果
只读(配置表) ✅ 效果好
读写比 10:1 ⚠️ 效果一般
读写比 1:1 ❌ 负面效果
写多于读 ❌ 严重负面

对写频繁的表,每次写入都要清空缓存,而清空操作需要独占缓存锁,写操作越多,锁竞争越激烈,性能反而下降

官方测试数据

MySQL 官方在 8.0 发布时公布的数据:

在典型的高并发 OLTP 场景中,启用查询缓存的性能比禁用还差 10-30%

替代方案

1. 应用层缓存(推荐)

# 使用 Redis/Memcached 在应用层缓存
def get_user(user_id):
    cache_key = f"user:{user_id}"
    user = redis.get(cache_key)
    if user:
        return json.loads(user)

    user = db.query("SELECT * FROM user WHERE id = %s", user_id)
    redis.setex(cache_key, 3600, json.dumps(user))
    return user

2. MySQL 8.0 内置缓存替代

-- 8.0 弃用查询缓存,但提供了:
-- 1. InnoDB Buffer Pool(数据页缓存)
-- 2. 预编译语句缓存
-- 3. 表缓存

-- 这些比查询缓存高效得多
SET GLOBAL innodb_buffer_pool_size = 4 * 1024 * 1024 * 1024;  -- 4GB

面试要点

  • 根本原因:收益远小于代价,在写频繁场景甚至导致性能下降
  • 失效机制:任何对表的 DML 操作都清空该表所有缓存
  • 锁竞争:缓存检查和清空都需要加锁
  • 替代方案:应用层 Redis 缓存 + InnoDB Buffer Pool
  • 8.0 移除设计:MySQL 团队认为这个特性是”负优化”

一句话总结:查询缓存就像”把水倒进漏水的桶”——不仅没存住,搬桶还费劲。

© 版权声明
THE END
喜欢就支持一下吧
点赞15 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容