一、缓存三大问题是什么
引入 Redis 后最常踩的三个坑:穿透、击穿、雪崩。名字像,但成因和解法不同。
| 问题 | 成因 | 特征 |
|---|---|---|
| 穿透 | 查不存在的数据,缓存和库都没有 | 请求直击数据库,恶意可用不存在 key 打垮 DB |
| 击穿 | 某个热点 key 过期瞬间,大量请求同时回源 | 单点热点失效,并发压到 DB |
| 雪崩 | 大量 key 同一时间集中过期 / 缓存宕机 | DB 压力陡增甚至被打死 |
二、穿透:让”查不到”也有缓存
- 缓存空值:查不到也写个短 TTL 的空对象,拦住重复查询;
- 布隆过滤器:前置一层,根本不存在的 key 直接拒绝。
def get_user(uid):
val = redis.get(f"user:{uid}")
if val is not None:
return None if val == b"NULL" else json.loads(val)
row = db.query(uid)
if row is None:
redis.setex(f"user:{uid}", 60, "NULL") # 缓存空值 60s
return None
redis.setex(f"user:{uid}", 3600, json.dumps(row))
return row
三、击穿:热点 key 别让它”集体回源”
- 互斥重建:发现缓存失效,只放一个线程去查库,其余等待;
- 逻辑过期:key 不真过期,value 带过期时间字段,异步刷新。
四、雪崩:别让过期时间”约好一块走”
- 过期时间加随机:TTL = 基础 + 随机抖动,错开失效;
- 多级缓存:本地缓存(Caffeine) + Redis,Redis 挂了还有本地兜底;
- 限流降级:缓存故障时对回源做限流,保护 DB。
五、小结
穿透靠”空值 + 布隆”,击穿靠”互斥 + 逻辑过期”,雪崩靠”随机 TTL + 多级 + 限流”。三者都做好了,缓存才真正扛压而不是制造事故。