深色模式
缓存击穿与雪崩排查
摘要:缓存事故有三种不同形态:击穿(单个热 key 失效)、雪崩(大批 key 同时过期)、穿透(查询根本不存在的 key)。三者现象都是数据库压力暴涨,但处置方式完全不同。
适用环境
bash
redis-cli --version
redis-cli -h <host> -p 6379 info server | head -5
redis-cli -h <host> -p 6379 config get maxmemory-policy1
2
3
2
3
排障步骤
第 1 步:确认是缓存问题还是数据库问题
bash
redis-cli -h <host> info stats | grep -E 'keyspace_hits|keyspace_misses'
redis-cli -h <host> info commandstats | grep -i get1
2
2
命中率骤降(keyspace_misses 占比升高)说明大量请求穿透到数据库。
第 2 步:看 Redis 自身是否健康
bash
redis-cli -h <host> info stats | grep -E 'instantaneous_ops_per_sec|rejected_connections|evicted_keys'
redis-cli -h <host> slowlog get 20
redis-cli -h <host> --latency-history -i 51
2
3
2
3
evicted_keys 增长说明内存不足被逐出,rejected_connections 说明连接数已达上限。
第 3 步:区分三种故障形态
bash
# 看是否有大量 key 同时过期:抽样查看不同 key 的 TTL 分布
redis-cli -h <host> --scan --pattern 'cache:*' --count 1000 | head -20 | while read k; do
echo "$k $(redis-cli -h <host> ttl "$k")"
done1
2
3
4
2
3
4
- TTL 高度集中在同一数值 → 雪崩风险
- 单个 key 的 QPS 极高 → 击穿
keyspace_misses高且 key 根本不存在 → 穿透
第 4 步:找出热 key 与大 key
bash
redis-cli -h <host> --hotkeys # 需 maxmemory-policy 为 lfu 类
redis-cli -h <host> --bigkeys
redis-cli -h <host> info commandstats | sort -t= -k2 -rn | head -101
2
3
2
3
--hotkeys 依赖 LFU 淘汰策略
--hotkeys 需要 maxmemory-policy 为 allkeys-lfu 或 volatile-lfu,否则结果不可信。
第 5 步:处置 —— 击穿
bash
# 热 key 立即回填,避免所有请求打到数据库
redis-cli -h <host> set 'cache:hot:<id>' '<value>' EX 6001
2
2
长期方案:热 key 永不过期 + 异步刷新,或用互斥锁保证只有一个线程回源重建。
第 6 步:处置 —— 雪崩与穿透
bash
# 给已有 key 打散过期时间(示例:在原 TTL 上加随机偏移)
redis-cli -h <host> --scan --pattern 'cache:*' --count 1000 | while read k; do
ttl=$(redis-cli -h <host> ttl "$k")
[ "$ttl" -gt 0 ] && redis-cli -h <host> expire "$k" $(( ttl + RANDOM % 600 ))
done1
2
3
4
5
2
3
4
5
批量 SCAN + EXPIRE 会拖慢 Redis
在大实例上全量扫描会造成明显延迟上升。应限流分批执行,且避开业务高峰。
穿透的处置是把"查不到的结果"也缓存一个短 TTL 空值,或在入口做参数合法性校验与布隆过滤。
验证
bash
redis-cli -h <host> info stats | grep -E 'keyspace_hits|keyspace_misses|instantaneous_ops_per_sec'
redis-cli -h <host> dbsize1
2
2
常见坑
缓存与数据库双写不一致
先删缓存再改库、或先改库再删缓存,在并发下都有窗口期。应明确采用延迟双删或订阅 binlog 的方式。
重建缓存未做并发控制
key 失效瞬间成千上万请求同时回源,数据库瞬间被打爆,这正是击穿的本质。
空值缓存忘记设 TTL
把"查不到"永久缓存,数据真正写入后永远读不到,造成长期数据不一致。
缓存整体不可用时不降级直接放开流量
缓存集群故障时若无兜底,数据库会立刻被打垮。应提前准备限流与降级开关。