一、什么是 Redis 雪崩
Redis 雪崩 指的是:在某一时刻,大量缓存同时失效 ,或者 Redis 集群整体宕机,导致所有请求直接涌向数据库,数据库瞬间被打垮,进而引发整个服务链路崩溃。
雪崩的两种触发形式
| 触发形式 | 说明 |
|---|---|
| 大量 Key 同时过期 | 例如批量缓存设置了相同的 TTL,到期瞬间集体失效 |
| Redis 集群故障 | 主节点宕机、网络分区、机房断电等导致 Redis 不可用 |
二、雪崩 vs 击穿 vs 穿透(快速区分)
| 问题 | 触发条件 | 影响范围 |
|---|---|---|
| 雪崩 | 大量 Key 同时失效 / Redis 宕机 | 整个系统,大量请求 |
| 击穿 | 单个热点 Key 过期,被高并发同时访问 | 单个热点数据 |
| 穿透 | 查询不存在的数据(如 ID=-1) | 绕过缓存直接查库 |
雪崩是系统性的,影响面最大。
三、服务降级的核心思想
当 Redis 雪崩发生时,系统资源(数据库连接、CPU、内存、带宽)已经处于极度紧张甚至崩溃边缘。此时,服务降级的核心逻辑是:
牺牲非核心功能,保障核心功能的可用性;牺牲数据实时性,保障系统不宕机。
降级不是"修复 Redis",而是在 Redis 不可用的窗口期内,让系统以一种"有损"但"可用"的状态继续运行,避免级联故障。
四、具体的降级策略
1. 页面降级(前端/网关层)
当检测到 Redis 异常时,前端或网关直接返回静态兜底页面,不再向后端请求动态数据。
用户请求商品详情页
↓
网关检测到 Redis 响应超时/错误率飙升
↓
直接返回 CDN 上的静态商品页面(可能缺少实时价格、库存)
↓
用户看到"页面加载中"或简化版页面,而不是 502/504
实现方式:
-
网关层配置熔断规则(如 Hystrix、Sentinel)
-
当 Redis 调用失败率超过阈值,触发
Fallback逻辑 -
返回本地缓存的静态 HTML 或 CDN 地址
2. 接口降级(服务层)
对于不同的接口,定义不同的降级行为:
| 接口类型 | 正常逻辑 | 降级逻辑 |
|---|---|---|
| 核心读接口(如商品基本信息) | 读 Redis → 读数据库 | 直接读数据库 + 本地缓存 + 限流 |
| 非核心读接口(如推荐列表) | 读 Redis → 读数据库 | 返回空列表 / 返回默认推荐 |
| 写接口(如加购物车) | 写 Redis + 异步落库 | 直接写数据库 / 放入消息队列延迟处理 |
关键原则 :核心接口尽量保,非核心接口直接丢。
3. 数据降级(返回有损数据)
Redis 雪崩后,数据库可能扛不住全量查询。此时可以:
-
返回缓存中的旧数据 :如果本地缓存(如 Caffeine、Guava Cache)还有数据,直接返回,哪怕已经过期几分钟
-
返回默认值:例如库存显示"有货"而不是精确数字,价格显示上次缓存的价格
-
返回简化数据:商品详情页只返回标题和主图,不返回规格参数、评价等
正常流程: 请求 → Redis → 返回完整商品数据
降级流程: 请求 → Redis 失败 → 查本地缓存(可能过期) → 返回"过期但可用"的数据
本地缓存也没有 → 返回默认兜底数据
4. 开关降级(配置中心动态控制)
通过配置中心(如 Nacos、Apollo)维护一组降级开关,在 Redis 雪崩时动态调整系统行为:
XML
# 降级配置示例
degrade:
redis:
enabled: true # 总开关:开启 Redis 降级模式
use_local_cache: true # 优先使用本地缓存
return_default: true # 无缓存时返回默认值
db_query_rate_limit: 50 # 数据库查询限流:每秒最多 50 次
skip_non_core: true # 跳过非核心接口的 Redis 查询
为什么用开关而不是自动熔断?
-
雪崩可能是短暂的(如网络抖动),自动熔断会误伤
-
开关可以由运维根据监控手动触发,也可以由自动化脚本触发
-
恢复时也可以逐步关闭开关,观察系统稳定性
五、降级的完整链路设计
java
用户请求
↓
【网关层】Sentinel/Hystrix 熔断检测
├── Redis 正常 → 正常路由到服务
└── Redis 异常 → 触发降级
↓
【服务层】读取降级开关配置
├── 核心接口
│ ├── 本地缓存有数据 → 返回本地缓存(可能过期)
│ └── 本地缓存无数据 → 限流查数据库 → 返回数据
│
└── 非核心接口
├── 返回默认值 / 空数据
└── 或直接拒绝请求(返回友好提示)
↓
【数据库层】通过限流保护数据库不被打垮
六、与限流、熔断的配合
服务降级不是孤立的,需要与以下机制配合:
| 机制 | 作用 | 与降级的关系 |
|---|---|---|
| 熔断 | Redis 故障时快速失败,不等待超时 | 熔断触发后,进入降级逻辑 |
| 限流 | 控制进入数据库的请求量 | 降级后,限流保护数据库 |
| 降级 | 降低服务质量,保障可用性 | 熔断 + 限流后的具体执行策略 |
| 预热 | Redis 恢复后逐步加载热点数据 | 降级结束后,通过预热恢复正常 |
七、代码层面的简单示例
以 Java + Sentinel 为例:
java
@SentinelResource(
value = "getProductInfo",
fallback = "getProductFallback" // 降级方法
)
public Product getProductInfo(Long productId) {
// 正常逻辑:先查 Redis
String cache = redisTemplate.opsForValue().get("product:" + productId);
if (cache != null) {
return JSON.parseObject(cache, Product.class);
}
// 再查数据库
return productDao.findById(productId);
}
// 降级方法:Redis 异常时执行
public Product getProductFallback(Long productId, Throwable ex) {
// 1. 尝试本地缓存
Product local = localCache.getIfPresent(productId);
if (local != null) {
return local; // 返回可能过期的数据
}
// 2. 返回默认兜底数据
return Product.defaultProduct(productId);
}
八、总结
| 维度 | 要点 |
|---|---|
| 降级的本质 | 用"有损服务"换"系统存活" |
| 降级的时机 | Redis 大量失效 / 集群宕机 / 响应超时剧增 |
| 降级的手段 | 页面静态化、接口返回默认值、本地缓存兜底、非核心功能关闭 |
| 降级的原则 | 核心保活、非核心可丢;能缓存就不查库;能返回旧数据就不返回错误 |
服务降级是 Redis 雪崩时的最后一道防线。
真正好的架构,还需要在事前做好Key 过期时间打散 、多级缓存 、Redis 高可用集群等预防措施。