Redis缓存击穿:热点Key过期打崩数据库的3种解决方案

Redis 缓存击穿:热点 Key 过期打崩数据库的 3 种解决方案

缓存击穿、缓存穿透、缓存雪崩经常被混淆。本文用一个「首页热点配置」的真实故障,讲清楚三者的区别,重点给出缓存击穿的 3 种解决方案(互斥锁 / 逻辑过期 / 异步重建),并附可直接使用的代码。

一、问题案例

首页有一个「今日推荐」接口,查一个配置对象,QPS 数千。为了抗压,我给这个配置加了 Redis 缓存,过期时间 10 分钟。

上线第二天晚上,数据库连接数突然打满,首页接口大面积超时。监控显示:DB 的 QPS 从几百瞬间飙到几万,持续几秒又回落。

排查后发现,出问题的只有「今日推荐」这一个 key------它在过期的那一秒,几千个请求同时发现缓存没了,全部穿透到数据库。这不是雪崩,是击穿。

二、原理详解:击穿、穿透、雪崩的区别

类型 触发点 范围 典型场景
击穿 单个热点 key 过期瞬间被并发穿透 单个 key 热门商品、首页配置
穿透 查询一个根本不存在的 key 单个 key 恶意请求、不存在的 id
雪崩 大量 key 同时过期,或 Redis 宕机 全局 过期时间没打散、缓存层故障

击穿的关键特征:热点 key + 过期瞬间 + 高并发。三者缺一不可。普通 key 过期被穿透,压力不大;热点 key 但没并发,也不会打崩。

三、实战代码:3 种解决方案

方案一:互斥锁(最常用)

缓存未命中时,只让一个请求去查库并重建缓存,其余请求等待:

java 复制代码
public String getConfig() {
    String v = redis.get("config:today");
    if (v != null) return v;

    // 只有一个请求能拿到锁去查库重建
    boolean locked = redis.setIfAbsent("config:today:lock", "1", 3, TimeUnit.SECONDS);
    if (locked) {
        try {
            v = db.query();
            redis.set("config:today", v, 10, TimeUnit.MINUTES);
        } finally {
            redis.delete("config:today:lock");
        }
    } else {
        // 拿不到锁,稍等后重查一次缓存
        Thread.sleep(50);
        v = redis.get("config:today");
    }
    return v;
}

适用:数据强一致、允许短暂等待。缺点:拿锁失败的请求要重试,代码稍繁琐。

方案二:逻辑过期(不物理删除 key)

key 永不物理删除,值里存一个「逻辑过期时间戳」。读到已逻辑过期的值,先返回旧值顶着,后台异步重建:

java 复制代码
class ConfigData {
    String value;
    long expireAt; // 逻辑过期时间戳
}

public String getConfig() {
    ConfigData data = redis.get("config:today");
    if (data == null) return rebuildWithLock();
    if (System.currentTimeMillis() < data.expireAt) return data.value;

    // 已逻辑过期:先返回旧值,异步重建
    asyncRebuild();
    return data.value;
}

适用:热点数据、对实时性不敏感。优点:用户永远不等待;缺点:会短暂返回旧值。

方案三:永不过期 + 后台定时刷新

热点 key 直接不设过期时间,用一个定时任务或消息队列,在数据变更时主动更新缓存。适合「数据量小、变更少、必须强一致」的配置类场景。

四、延伸:顺带防穿透

穿透(查不存在的 key)的防法:

  1. 空值缓存:把「不存在」也缓存起来(短过期时间),避免每次都打库。
  2. 布隆过滤器:在缓存前用布隆过滤器拦截不存在的 key。

五、总结

  • 击穿、穿透、雪崩是三种不同的问题,先分清再下手:击穿是"一个热点 key 过期",穿透是"查不存在的 key",雪崩是"一大片同时失效或缓存挂了"。
  • 击穿首选互斥锁(简单可靠),热点数据可用逻辑过期(不卡用户)。
  • 顺手把空值缓存或布隆过滤器补上,挡住穿透。

我是无羡(小剑),全栈偏后端的独立开发者。

作品集:无羡 · 独立开发者作品集

如果对你有帮助,欢迎点赞、收藏、关注。

相关推荐
AIgorithmGEEK6 小时前
MySQL 索引全解(下):建、查、删实战与知识速查表
数据库·mysql·b+树·索引
行百里er8 小时前
Redis 完结篇——Redis 热点问题在 Spring Boot 项目中实战
redis·后端
唠点键盘之外的8 小时前
26 向量数据库实战:从 0 搭一条能用的检索链路(pgvector + Spring AI)
数据库·人工智能·spring
云和恩墨8 小时前
数据库监控简史:监控不止于告警——从图表大爆发到健康管理闭环
数据库
ly76899 小时前
MongoDB 事务开销实测:快照隔离下的写冲突、重试与分片事务边界
数据库·mongodb·事务·分片集群·快照隔离·写冲突
豆角焖肉9 小时前
Spring AOP 自定义注解实现系统操作日志
数据库·自定义注解·springaop·操作日志·aop 切面
金伟API102410 小时前
不会写复杂SQL怎么办?
数据库·sql
执笔为剑10 小时前
2.1.2-1、V8R3迁移到V8R6-Oracle兼容特性
数据库
geovindu11 小时前
sql:Search-Optimized SQL Queries using mysql 9.0
android·数据库·sql·mysql·数据库开发·数据库架构
for_ever_love__11 小时前
MySQL SELECT 与 WHERE 优化实战:可搜索条件、联合索引与执行计划
数据库·mysql·oracle·select·where·条件优化·查询优化技巧