缓存穿透、击穿、雪崩需区分处理。穿透通过参数校验、缓存空值及布隆过滤器应对;击穿用互斥锁或逻辑过期方案;雪崩结合随机TTL、数据预热及本地缓存限流等机制。面试时应结合项目细节,而非仅背诵名词。
最近翻阅了不少Java后端秋招提前批的面经,发现Redis被问得相当频繁。不是那种“Redis有哪些数据结构”的泛泛提问,而是直接从项目场景切入:你简历上写了缓存优化,那缓存穿透、击穿、雪崩分别怎么处理?为什么这么处理?线上真的这么操作过吗?
这道题看起来像经典八股,但其实很容易被追问到细节。很多人一上来就背“穿透用布隆过滤器,击穿用互斥锁,雪崩加随机过期时间”,面试官再追问两句,往往就卡住了。
长期稳定更新的攒劲资源: >>>点此立即查看<<<
更推荐的做法,是顺着「一次请求到底是怎么走的」来组织回答。
先说缓存穿透。它指的是,请求的数据在数据库里压根就不存在。比如一直有人拿一个不存在的商品ID去查询,缓存里没有,数据库里也没有,每次请求都直接穿透到数据库。这才是真正的穿透。
缓存击穿则不同。它说的是一个热点key突然过期了。比如首页推荐配置、爆款商品的库存、秒杀活动的信息,平时这些数据都被缓存扛住了,但某一刻这个key失效了,大量请求瞬间一起冲到数据库。
至于缓存雪崩,是一批 key 在同一时间点集体失效,或者 Redis 集群本身短时间不可用。这不是单个热点 key 的问题,而是缓存层大面积失守。
面试时,先把这三个概念的区别说清楚,后面才有得聊。否则你把击穿也说成穿透,面试官基本会默认你只是背过题。
处理穿透的第一层,其实是参数校验。
如果业务上商品 id 必须是正整数,那负数、超长字符串、明显不合法的枚举值,应该在入口处就直接拦截掉。不要让这类请求进入缓存层,更不要让它打到数据库。
第二层才是缓存空值。数据库查不到结果时,可以把空结果也写进缓存,过期时间设短一些,比如几十秒到几分钟。这样,同一个不存在的 key 就不会反复穿透数据库。
布隆过滤器更适合那些更重的场景:数据集合比较稳定、非法 key 数量很多、数据库压力已经明显上来了。它的特点是可能误判“存在”,但不会把真实存在的数据误判成不存在。所以用它做前置过滤可以,但不能把它当成最终真相。
一个比较稳妥的回答可以这样组织:
如果面试官继续追问“缓存空值会不会污染Redis”,可以这样回应:空值的 TTL 要短、value 要小,并且只对高频不存在的 key 做缓存,而不是所有 miss 都无脑写进去。
热点 key 过期时,最怕的就是一堆线程同时去查数据库、同时回写缓存。
常见的做法是加互斥锁:只有抢到锁的线程去查数据库并重建缓存,其他线程稍等后重试,或者直接返回旧值。
这里有一个常见的坑:别说“SETNX 后再 EXPIRE”。这两个命令拆开执行不是原子操作,中间如果进程挂了,就可能留下死锁。Redis 里更常见的做法是使用一条 SET 命令,带上 NX 和 EX 参数,把“只在不存在时设置”和“过期时间”放到同一个原子命令里。解锁时还要校验 value,通常用 Lua 脚本保证只删除自己持有的锁。
如果业务允许短暂使用旧数据,逻辑过期方案会更舒服:缓存里不只放数据,还放一个逻辑过期时间。请求来了发现逻辑过期,不是立刻让所有人去查库,而是由一个线程异步去刷新,其他请求先拿着旧数据顶住。
这个方案在首页配置、榜单、推荐位上很常见。用户看到几十秒前的旧数据,问题不大,但数据库被打爆可是真事故。
面试时,可以这样组织回答:
这几句话,比单独背一个“互斥锁”要有分量得多。
造成雪崩的典型原因有两个。
一个是大量 key 同时过期。比如你批量导入缓存,全都设成 30 分钟,半小时后它们一起失效。这种情况用随机 TTL 就能缓解:在基础过期时间上加一段随机抖动,让 key 的失效时间分散开。
另一个是缓存服务本身不可用。这就不是随机 TTL 能解决的了。需要看系统有没有本地缓存、限流、降级、熔断,以及核心数据能不能提前预热。
面试时可以按层次来回答:
如果你的项目只是普通后台系统,没有高并发活动,也别硬编秒杀场景。可以说“我没做过秒杀,但在后台字典、配置缓存里会做随机 TTL,避免定时任务批量刷新导致同时过期”。这个回答反而更像真实的经验。
这道题到最后,通常会落到项目上。
如果你的简历里写了 Redis,就提前把这几个问题想清楚:
这些问题的价值,比背十页 Redis 面试题更重要。
侠游戏发布此文仅为了传递信息,不代表侠游戏网站认同其观点或证实其描述