缓存雪崩与解决方案

面试官问:"什么是缓存雪崩?"

小陈说:"就是缓存崩了?"

面试官追问:"如果 Redis 挂了,所有请求都打到数据库,会怎样?"

小陈说:"...数据库也崩了?"

面试官继续追问:"怎么防止这种情况?"

小陈答不上来。

缓存雪崩是 Redis 面试中的高频问题。这道题能说清楚"雪崩 vs 击穿"的区别,以及多重防护方案的候选人,对 Redis 的高可用架构有实战理解。

一、缓存雪崩的定义 🔴

1.1 两种雪崩场景

场景 1:大量 key 同时过期
Redis 缓存了 10000 个 key,过期时间都是 2024-01-01 00:00:00
00:00:00 时刻,所有 key 同时过期
→ 10000 个请求同时查缓存 miss
→ 10000 个请求同时打数据库
→ 数据库压力过大,雪崩

场景 2:Redis 服务不可用
Redis 挂了(宕机/网络分区)
→ 所有请求无法命中缓存
→ 所有请求直接打数据库
→ 数据库压力过大,雪崩

1.2 雪崩 vs 击穿

问题原因影响范围解决方案
击穿热点 key 过期单个 key互斥锁、逻辑过期
雪崩大量 key 同时过期/Redis 挂了多个 key 或全量过期时间随机、多级缓存、限流

1.3 ❌ 错误示范

候选人原话:"雪崩就是击穿,热点 key 过期了就是雪崩。"

问题诊断:混淆了两个概念。击穿是单个热点 key,雪崩是大量 key 同时失效(或者 Redis 不可用)。

【面试官心理】 这道题我会从"击穿和雪崩的区别"追问。能说清楚"击穿是单个,雪崩是大量"的候选人,说明他对缓存问题有系统理解。

二、过期时间随机化 🔴

2.1 解决方案

# ❌ 错误:固定过期时间
redis.setex('user:1', 3600, user_data)
redis.setex('user:2', 3600, user_data)  # 都在同一时刻过期

# ✅ 正确:过期时间随机
redis.setex('user:1', 3600 + random.randint(0, 600), user_data)  # 1小时 + 0~10分钟
redis.setex('user:2', 3600 + random.randint(0, 600), user_data)

2.2 批量导入时的处理

# 批量导入 key 时,打散过期时间
keys = ['user:1', 'user:2', 'user:3', ...]
for key in keys:
    # 过期时间 = 基础时间 + 随机偏移
    ttl = BASE_TTL + random.randint(0, int(BASE_TTL * 0.2))
    redis.setex(key, ttl, value)

三、多级缓存架构 🟡

3.1 本地缓存 + Redis

# 两级缓存架构
local_cache = {}  # 本地缓存(L1)

def get_user(user_id):
    cache_key = f'user:{user_id}'

    # L1 本地缓存(毫秒级)
    if user_id in local_cache:
        return local_cache[user_id]

    # L2 Redis
    user = redis.get(cache_key)
    if user:
        local_cache[user_id] = user  # 回填 L1
        return user

    # 数据库
    user = db.get(f'SELECT * FROM users WHERE id={user_id}')
    if user:
        redis.setex(cache_key, 3600, user)
        local_cache[user_id] = user
    return user

3.2 限流保护

# 接口限流(Redis 计数器)
def rate_limit(user_id):
    key = f'rate:{user_id}'
    count = redis.incr(key)
    if count == 1:
        redis.expire(key, 1)  # 每秒重置
    return count <= 100  # 每秒最多 100 次

# Redis 不可用时的降级
def get_user_fallback(user_id):
    # 降级:直接查数据库,但限制 QPS
    if rate_limit(user_id):
        return db.get(f'SELECT * FROM users WHERE id={user_id}')
    return None

四、Redis 高可用 🟡

4.1 Redis Sentinel(哨兵)

# Redis Sentinel 架构:
# 主库 → 从库1
#      ↘ 从库2
#              ↓
#          Sentinel 集群(监控 + 自动故障转移)

4.2 Redis Cluster

# Redis Cluster 架构:
# 节点1 → 节点2
#   ↓↗   ↓↘
# 节点3 → 节点4
# 16384 个 hash slot 自动分片

【面试官心理】 缓存雪崩的解决方案需要从多个层面考虑:过期时间、限流、高可用。能说出完整方案的候选人,说明他对 Redis 的生产架构有系统理解。


级别考察重点期望回答
P5问题认知雪崩的定义,和击穿的区别
P6解决方案过期时间随机、两级缓存
P7高可用Sentinel/Cluster、限流降级