Redis缓存三连击:击穿、雪崩、穿透,一次讲透!
双十一大促零点,你手速拉满抢限量神券——结果页面卡死、刷新失败、“系统繁忙”弹窗反复刷屏。后台可能正被三股力量轮番暴击:有人用脚本狂刷根本不存在的用户ID(穿透);爆款商品详情页在缓存过期那一毫秒,被上万请求同时“凿穿”(击穿);而整个商品库的缓存Key又恰巧在整点集体失效(雪崩)。这三者不是玄学黑话,而是真实可复现、可定位、可防御的典型故障链。
一、缓存穿透:查不到,还拼命查
定义:查询压根不存在的数据(比如user:999999999),缓存无记录,请求直冲数据库,高频无效查询拖垮DB。
▶️ 极简复现:
EXISTS user:999999999 # 返回0,但每秒1万次?DB瞬间变筛子。
✅ 防御双保险(Python示例):
import redis, hashlib
from bloom_filter import BloomFilter
r = redis.Redis()
bf = BloomFilter(capacity=1000000, error_rate=0.001)
def get_user(user_id):
key = f"user:{user_id}"
# 1. 布隆过滤器快速拦截绝对不存在的key
if not bf.contains(key):
return None
# 2. 查缓存
data = r.get(key)
if data:
return data
# 3. 缓存未命中,查DB(此处省略具体实现)
db_data = query_db_user(user_id)
if db_data:
r.setex(key, 3600, db_data) # 正常数据缓存1小时
else:
r.setex(key, 300, "NULL") # 空值缓存5分钟,防反复穿透
return db_data
核心就两点:存在性前置校验 + 空值短期缓存,把无效流量挡在数据库门外。
二、缓存击穿:热点Key过期的“生死1毫秒”
定义:某个超高频Key(如首页推荐位hot_item)恰好过期,瞬间大量并发请求穿透缓存,争抢重建,数据库CPU飙升。
▶️ 复现场景:
GET hot_item 返回 nil 的刹那,1000个线程同时执行SET——谁先写?谁来查库?没协调就是灾难。
✅ 加锁重建(Python + Redis SETNX):
def get_hot_item():
key = "hot_item"
data = r.get(key)
if data:
return data
lock_key = f"lock:{key}"
# 尝试加分布式锁(带自动过期,防死锁)
if r.set(lock_key, "1", nx=True, ex=5):
try:
# 二次检查:防止重复重建
data = r.get(key)
if not data:
data = build_hot_item_from_db()
r.setex(key, 3600, data)
return data
finally:
r.delete(lock_key) # 必须释放锁
else:
# 未抢到锁,短暂等待后重试(或返回旧缓存/降级数据)
time.sleep(0.01)
return get_hot_item()
关键点:锁粒度最小化、自动过期、二次检查、异常必释放。
三、缓存雪崩:大批Key集体“下班”
定义:大量Key设置相同过期时间(如凌晨2点统一TTL=3600),到期后集中失效,流量洪峰同步涌向数据库。
▶️ 探测风险(仅限开发环境!):
KEYS item:* # ⚠️ 生产禁用!更安全方式:SCAN + TTL采样
✅ 根治方案(Python):
import random
def set_item_with_jitter(key, value, base_ttl=3600):
jitter = random.randint(-int(base_ttl*0.1), int(base_ttl*0.1))
final_ttl = max(600, base_ttl + jitter) # 不低于10分钟
r.setex(key, final_ttl, value)
# 核心数据兜底:逻辑永不过期 + 异步刷新
def set_essential_item(key, value):
r.set(key, value)
# 后台任务每30分钟主动更新一次
口诀记牢:不设固定过期时间,只设“基础TTL+随机抖动”;核心数据宁可冗余更新,也不集体断供。
四、真实踩坑与监控预警实战
我们曾在线上活动遭遇穿透事故:未对注册手机号做空值缓存,黑产脚本遍历user:138****0001~138****9999,DB连接数10秒飙至2000+,服务全面超时。
如何提前发现?两个低成本监控项:
-
Redis慢日志:执行
SLOWLOG GET 5,若频繁出现GET命令+大量nil响应,大概率有穿透苗头; -
客户端错误率:用Prometheus监控
redis_connection_errors_total,配置告警规则:rate(redis_connection_errors_total[5m]) > 0.1→ 立即排查是否雪崩前兆。
监控不是锦上添花,而是故障前的最后一道哨兵。
结语:稳如磐石的缓存铁三角
穿透、击穿、雪崩,本质是缓存与数据一致性之间的三道裂缝。守住它们,只需三招:
🔹 存在性校验——布隆过滤器+空值缓存,让“不存在”止步于缓存层;
🔹 热点保护——原子锁+双重检查,把重建动作串行化;
🔹 过期分散——随机TTL+异步刷新,避免缓存集体休眠。
缓存不是银弹,而是需要精心设计的防护体系。你在项目中用过哪种防护组合?欢迎在评论区晒出你的redis.conf关键配置或自研工具片段——Dev.to的极客们,正等着抄作业呢!
Top comments (0)