Redis 缓存穿透、击穿、雪崩:三种故障的边界与兜底
这三个词经常被混着用,但它们解决的是不同问题。区分清楚才不会上错方案。
一、穿透:查一个数据库里根本不存在的 key。缓存必然不命中,请求全部打到数据库。如果有人拿随机 ID 刷接口,数据库会被打穿。两个解法:一是缓存空值,查不到也写一个短过期的空标记,缺点是会占内存,被刷时要用布隆过滤器兜一层;二是布隆过滤器前置,把所有存在的 key 哈希进位数组,不存在的直接拦掉。布隆过滤器有误判率,判断存在可能不存在,判断不存在一定不存在,所以只能用来挡穿透,不能替代真实查询。
二、击穿:单个热点 key 突然过期,瞬间大量并发同时回源。注意是单个 key,不是大面积。解法是互斥重建:只让一个请求去查数据库并回填,其他请求等待或者返回旧值。用 Redis 的 setnx 加过期时间做分布式锁,注意锁要设超时,否则持有者挂掉就死锁。另一种是逻辑过期,value 里存一个过期时间戳但不设 TTL,读到逻辑过期后异步刷新,返回旧值,可用性优先的场景更合适。
三、雪崩:大量 key 在同一时间点集中失效,或者 Redis 整体挂掉。前者是因为批量预热时 TTL 设成一样,解法是给过期时间加随机偏移,比如基础 30 分钟加 0 到 5 分钟的随机值。后者要靠架构:主从加哨兵或者集群、本地缓存做二级兜底、限流降级保护数据库。数据库前面加一层熔断,Redis 挂了也不至于把库打穿。
排查顺序:先看 Redis 命中率,命中率骤降说明有穿透或者大面积过期;再看数据库 QPS 曲线,尖峰对应击穿或者雪崩;最后看慢查询和连接数,连接数打满通常是穿透。
几个容易写错的地方:空值缓存一定要设过期,不设就等于内存泄漏;互斥锁的 key 要带业务前缀,避免和正常缓存冲突;逻辑过期方案里刷新失败的 key 要能重试,不然会一直返回旧值;预热脚本要做分批,别一次性灌几十万 key 把 Redis 内存打满。
- 点赞
- 收藏
- 关注作者
评论(0)