Redis 缓存击穿(Cache Breakdown)是指在高并发场景下,某个热点 Key(Hot Key)突然过期,此时大量请求同时访问该 Key,由于缓存中不存在该数据,所有请求直接穿透到数据库,导致数据库瞬间压力过大,甚至宕机。

这与“缓存穿透”(查询不存在的数据)和“缓存雪崩”(大量 Key 同时过期或 Redis 宕机)有所不同,击穿的核心特征是:单个热点 Key 过期 + 高并发。

以下是针对缓存击穿的详细解析及解决方案:

核心成因
热点数据:某些数据访问频率极高(如秒杀商品详情、突发新闻)。
过期时间集中:该热点 Key 设置了固定的过期时间,一旦到期,缓存失效。
并发量大:在 Key 失效的瞬间,成千上万的请求同时到达,发现缓存为空,转而查询数据库。

主流解决方案

方案一:互斥锁(Mutex Lock / Distributed Lock)—— 最常用
这是解决缓存击穿最经典的方法。当缓存失效时,不是所有线程都去查数据库,而是只让一个线程去查,其他线程等待。

实现逻辑:
请求访问缓存,发现 Key 不存在。
尝试获取分布式锁(如基于 Redis 的 SETNX 或 Redisson 锁)。
获取锁成功的线程:去查询数据库,将数据写入缓存,然后释放锁。
获取锁失败的线程:休眠一小段时间后重试,重新从缓存读取数据(此时大概率已被第一个线程写入)。
优点:保证数据库只被访问一次,有效保护后端。
缺点:性能略有下降(因为要串行化),如果构建缓存耗时很长,会导致其他请求等待时间过长。
代码逻辑示例(伪代码):
String value = redis.get(key);
if (value == null) {
// 尝试获取互斥锁
if (tryLock(“lock:” + key)) {
try {
// 双重检查,防止锁等待期间其他线程已重建缓存
value = redis.get(key);
if (value == null) {
value = db.query(key);
redis.set(key, value, expireTime);
}
} finally {
unlock(“lock:” + key);
}
} else {
// 获取锁失败,休眠重试
Thread.sleep(50);
return getData(key); // 递归或循环重试
}
}
return value;

方案二:逻辑过期(Logical Expiration)—— 高性能场景
适用于对一致性要求不高,但要求极高可用性和低延迟的场景(如微博热搜、新闻头条)。数据在 Redis 中永不过期,但在 Value 内部包含一个逻辑过期时间字段。

实现逻辑:
存入数据时,不设置 Redis 的 TTL,而是在 Value 对象中包含一个 expireTime 字段。
读取数据时,判断 当前时间 > 逻辑过期时间。
如果未过期:直接返回数据。
如果已过期:
尝试获取互斥锁。
获取锁成功:开启一个独立线程(异步)去查询数据库并更新缓存,主线程直接返回旧的脏数据(保证响应速度)。
获取锁失败:直接返回旧的脏数据。
优点:用户请求无需等待数据库查询,响应极快,彻底避免阻塞。
缺点:会返回一段时间内的旧数据(弱一致性),实现复杂度较高(需处理异步构建)。

方案三:预热(Cache Warming)
在系统上线前或高峰来临前,通过脚本或定时任务将热点数据提前加载到缓存中,并设置较长的过期时间或永不过期。

适用场景:可预知的热点(如双11大促商品、春节红包活动)。
局限:无法应对突发的、不可预知的热点。

方案四:多级缓存
引入本地缓存(如 Caffeine、Guava)作为一级缓存,Redis 作为二级缓存。
即使 Redis 中的热点 Key 过期,大部分请求会被本地缓存拦截,只有少量请求穿透到 Redis 层,从而减轻数据库压力。

方案对比总结
方案 一致性 性能/响应速度 实现复杂度 适用场景
互斥锁 强一致 中等(需等待锁) 低 对数据一致性要求较高的业务(如库存、账户信息)

逻辑过期 弱一致(短暂脏读) 极高(无阻塞) 高 对实时性要求不高,但吞吐量极大的业务(如资讯、热搜)

缓存预热 强一致 高 中 可预知的固定热点

最佳实践建议
默认首选互斥锁:对于大多数业务场景,使用 Redisson 等成熟的分布式锁框架实现互斥锁是最稳妥的方案。
设置随机过期时间:虽然这主要解决“缓存雪崩”,但在一定程度上也能分散热点 Key 的失效时间点,降低击穿概率。
兜底策略:在数据库层做好限流和熔断保护,即使缓存防线失守,也要防止数据库被彻底打挂。
监控告警:实时监控 Redis 的命中率和大 Key 情况,及时发现异常流量。

如果您需要具体的 Java (Spring Boot + Redisson) 或 Go 语言的代码实现示例,请告诉我!

Logo

欢迎加入 MCP 技术社区!与志同道合者携手前行,一同解锁 MCP 技术的无限可能!

更多推荐