这三个场景是缓存的经典失效模式,后端面试几乎必问。考察你能不能预见攻击模式和流量尖峰——是真的设计过缓存系统,还是只背过概念。
先把三个概念讲清楚——穿透:查询缓存和DB都不存在的数据,缓存永远填不上,每次都打DB。击穿:某个热点key刚好过期,大量并发请求同时打到DB。雪崩:大量key同时过期(或Redis本身挂了),DB被打爆。
穿透解法:缓存空值短过期时间,或者在缓存前加布隆过滤器,已知不存在的key直接拒绝,连Redis都不用查。
击穿解法:用互斥锁(SETNX),只允许一个线程去DB重建缓存,其他线程等待或返回旧值;或者热key设"逻辑永不过期",后台异步刷新。
雪崩解法:TTL加随机抖动(基础TTL + 随机偏移)避免同时过期;加本地多级缓存(Caffeine + Redis);DB侧加熔断和限流;Redis本身做高可用(哨兵/集群)防止缓存层整体崩溃。
串联总结:生产环境是多层防御——布隆过滤器+空值缓存防穿透、互斥锁防击穿、TTL抖动+本地缓存+熔断防雪崩。
这三个是不同的失效模式。缓存穿透是查询既不在缓存也不在DB的数据,比如攻击者不停请求/user/-1,缓存永远填不上,每次都打DB。两种防御:把null结果也缓存起来,设短TTL(比如60秒),重复攻击直接被Redis拦住;或者前置布隆过滤器,启动或写入时把所有有效ID加到过滤器里,查询前先过一遍,不存在直接拒绝。布隆过滤器会有误判(把不存在说成存在),但绝不会漏判,所以用来挡穿透是安全的。缓存击穿是数据本来存在,但某个热点key刚好过期,还没来得及重建,几千个并发请求全部打到DB。解法一是互斥锁——第一个miss的请求去抢一个短TTL的Redis锁,抢到的去重建,其他的等待或返回旧值;解法二是"逻辑永不过期",实际TTL不设,但value里带一个逻辑过期时间戳,读到过期就异步触发后台重建,其他请求继续读老数据。缓存雪崩是大量key同时过期,或者Redis整个挂了,全量读流量冲到DB把它打爆。防御是多层的:第一,TTL加抖动——别给所有key设相同的3600秒,而是3600 + random(0, 600),过期时间打散。第二,加本地进程内缓存(Java里用Caffeine),Redis挂了每个JVM还能靠自己的内存扛一分钟。第三,DB前加熔断和限流,DB扛不住时快速失败而不是堆积连接拖死整个系统。第四,Redis本身上Sentinel或Cluster,避免单点挂掉缓存层直接蒸发。生产环境我会把这些组合起来:布隆过滤器+空值缓存+TTL抖动+本地缓存+熔断,而不是只选一个。
面试官常把三个搞混,先清晰定义再讲解法。
能讲出布隆过滤器和它的误判特性,是加分项。
雪崩场景必须"TTL抖动+本地缓存+熔断"三层一起上,单独讲一个都不完整。
真实面试容易把击穿和雪崩说反,即答侠可以帮你临场确认用词。
占Redis内存——尤其被大量攻击时。用短TTL(60秒)和紧凑的空标记(空字符串)控制成本。
SET mutex:keyname 1 NX PX 30000。抢到的线程去DB重建,其他线程短暂sleep后重试。
预热只对启动/发布后有效,不能防批量TTL同时到期。预热要配合TTL抖动才完整。