DCS Redis 缓存实战:穿透、击穿、雪崩的三道防线
一、缓存出问题,往往不是 Redis 慢了,而是防线没建
很多团队用缓存的方式是这样的:业务先查 Redis,查不到再查数据库,查到就回填。平时跑得好好的,某天流量一上来,数据库突然被打满,服务跟着超时——回头一看,Redis 本身没慢,问题出在大量请求绕过了缓存,直接冲到了数据库。
缓存的三大经典故障——穿透、击穿、雪崩——名字很像,但成因和解法完全不同。穿透是查了不存在的数据,击穿是热点 Key 刚好过期,雪崩是一大批 Key 同时过期。下面按这三道防线展开。
二、第一道防线:防穿透——别让不存在的数据反复打数据库
缓存穿透是指查询一个数据库里根本不存在的数据:Redis 里没有,数据库里也没有,每次请求都要穿透缓存去查数据库。单个请求代价不大,但攻击者或异常流量用大量不存在的 ID 轮询时,数据库很快就被拖垮。
- 空值也要缓存:查不到时,把一个空值写进 Redis,并设一个较短的过期时间(比如 60 秒)。下次同样的请求直接命中空值,不再打数据库。注意空值的过期时间要比正常值短得多,避免正常数据写入后还长时间返回空。
- 布隆过滤器前置拦截:在 Redis 前面加一层布隆过滤器,先判断这个 Key 有没有可能存在。不存在就直接返回,不再往下查。布隆过滤器有少量误判(把不存在的判成存在),但不会把存在的判成不存在,所以适合做前置拦截。
- 参数校验先行:ID 格式明显非法的请求(比如负数、超长字符串),在网关或业务层直接拒绝,不要一路查到数据库。
关键动作:空值缓存是底线,布隆过滤器是加固。只做参数校验挡不住合法格式的恶意查询,空值缓存加布隆过滤器,才能把穿透的量级压下来。
三、第二道防线:防击穿——热点 Key 过期那一刻,别让所有请求一起冲数据库
缓存击穿是指某个热点 Key 刚好过期,而这时恰好有大量并发请求都在查它:Redis 里没了,所有请求同时回源数据库,瞬间把数据库打满。热点 Key 平时扛住了 99% 的流量,过期那一秒就成了单点。
- 热点 Key 永不过期,改用逻辑过期:把过期时间写在缓存值内部,而不是依赖 Redis 的 TTL。读到逻辑过期的数据时,先返回旧值,再由一个线程异步刷新缓存,其他请求继续用旧值,不会同时冲数据库。
- 互斥锁只放一个请求回源:第一个发现缓存缺失的请求先拿锁去查数据库、回填缓存,其他请求短暂等待后重试读缓存。锁要有超时和释放机制,避免拿锁的请求挂了之后所有人一直等。
- 热点 Key 提前预热:上线前、活动开始前,主动把热点数据刷进缓存;对已知会过期的热点 Key,在过期前主动刷新,不要等它自然过期。
关键动作:击穿防的是“同一时刻的并发回源”。逻辑过期适合读多写少的热点数据,互斥锁适合更新不频繁、能接受短暂等待的场景,两者可以结合用。
四、第三道防线:防雪崩——别让一大批 Key 在同一时刻集体过期
缓存雪崩是指大量 Key 在同一时间过期,或者 Redis 本身宕机:平时由缓存扛住的流量,在一瞬间全部落到数据库上。雪崩和击穿的区别在于规模——击穿是一个热点 Key,雪崩是一大批 Key。
- 过期时间加随机抖动:不要给一批 Key 设完全相同的 TTL。在基础过期时间上加一个随机值(比如 0 到 10 分钟),让它们错峰过期。这是成本最低、最先要做的一件事。
- 缓存分层、服务降级:关键接口保留本地缓存做第二层,Redis 不可用时先返回本地缓存的旧数据;同时准备降级开关,非核心接口在压力大时直接返回默认值或提示,把数据库资源留给核心接口。
- Redis 高可用和限流:生产环境用主备或集群模式,避免单点故障;数据库一侧做好连接池上限和慢查询监控,流量真的冲过来时,限流比硬扛更能保住核心服务。
关键动作:雪崩防的是“同一时刻的集体失效”。随机过期时间解决的是“同时过期”,高可用和降级解决的是“Redis 本身不可用”,两者缺一不可。
五、一个可落地的检查清单
- 查不到的数据有没有做空值缓存,空值过期时间是不是比正常值短得多?
- 热点 Key 有没有用逻辑过期或互斥锁,避免过期瞬间并发回源?
- 批量写入的 Key,过期时间有没有加随机抖动,而不是全部相同?
- Redis 有没有做主备或集群,单点故障时业务能不能降级?
- 数据库连接池、慢查询、缓存命中率有没有监控和告警?
- 有没有做过一次缓存失效演练,验证雪崩时降级开关是否真的生效?
一句话总结:缓存的三道防线,防的是三种不同的失效方式。穿透防不存在的数据,击穿防热点 Key 过期那一刻,雪崩防一大批 Key 同时失效——三道防线都建好,缓存才能真正替数据库扛住流量。
- 点赞
- 收藏
- 关注作者
评论(0)