缓存穿透

穿透,穿透,穿透;恶意,恶意,恶意的

定义

查询数据库中根本不存在的数据(如 id=-1 或已删除的用户),缓存不命中,每次都打到数据库。

解决方案

1️⃣ 缓存空值(Null Caching)

image-20260518214011253

2️⃣ 布隆过滤器(Bloom Filter)✅ 高频答案

启动时将所有合法 ID 载入 Bloom Filter(或 RedisBloom 模块)

布隆过滤器:“位数组(Bit Array)”+“多个哈希函数”

布隆过滤器的工作方式

当你把一个 Key 放入布隆过滤器时:

  1. 初始化:创建一个长度为 m的位数组(全是 0)。

  2. 多次哈希:使用 k个不同的哈希函数对 Key 进行计算,得到 k个位置索引。

  3. 置位:将位数组中这 k个位置的值全部设为 1

  4. 查询:再次用这 k个哈希函数计算新 Key,检查这 k个位置:

    • 只要有一个位置是 0绝对不在集合中(因为如果在,当时肯定会被置为1)。

    • 所有位置都是 1可能在集合中(因为可能是其他 Key 刚好把这些位置都置成了1,这就是误判)。

缓存雪崩

定义

大量缓存 Key 在同一时刻批量过期,或 Redis 服务整体宕机,导致几乎所有请求直接落到数据库。

两种情况:

  • 批量过期:促销商品缓存统一设 1h TTL → 整点失效
  • Redis 宕机:单机/主从故障无高可用

解决方案

1️⃣ TTL 错开(加随机值)

2️⃣ 热点数据永不过期

3️⃣ 做限流熔断

缓存击穿

定义

一个极热点的 Key 过期(或刚被逐出),此时有大量并发请求同时发现缓存 MISS,瞬间全部打到数据库重建缓存。

与雪崩不同:只有一个 Key,但该 Key QPS 极高。

解决方案

1️⃣ 互斥锁重建(分布式锁)✅ 最常用

为NULL的时候只有一个线程去查DB,查完构建缓存

image-20260518215101600

2️⃣热点 Key 永不过期 + 后台定时更新

缓存更新策略

旁路缓存

==先更新数据库,再删缓存==

理论上不会出现不一致的情况,因为操作数据库比操作缓存快很多,为确保一致性,可以加上过期时间

延迟双删

相对于旁路缓存来说,就是多了个等待一会再删

image-20260518223219805

问题

1. 删除失败了怎么办?

==首先最重要:如果注重缓存一致性,一定要设置保底的过期时间==

方案一:同步重试(简易版)

在代码逻辑中捕获删除异常的瞬间,立即进行几次重试

方案二:异步重试(推荐 ✅)

将删除失败的缓存 Key 放入一个消息队列(MQ)延迟队列(如 RocketMQ、RabbitMQ、甚至 Redis 自己的 List/Stream),由后台消费者异步重试删除。

方案三:基于 Binlog 的订阅

Canal中间件,伪装成成MySQL的Slave,订阅BinLog