Redis——缓存击穿问题
1、概述
如果说缓存穿透是用不存在的数据打穿防线,那缓存击穿就是盯着一个热点数据打穿防线。
举个最常见的例子:电商平台的秒杀商品 —— 比如一款限量 1000 件的低价手机,商品 ID=1001,这个 Key 会被百万用户频繁查询,是典型的热点数据,缓存里一直存着它的信息,数据库几乎不用处理这个商品的查询请求。
但缓存里的数据总有过期时间(TTL)。假设凌晨 2 点这个 Key 刚好过期,而此时恰好有 10 万用户同时刷新商品详情页—— 这 10 万请求会同时发现缓存里没有商品 1001 的数据,瞬间全部涌向数据库,请求查询同一条数据。
数据库原本对这个商品的查询压力几乎为 0,突然要承接 10 万并发查询,极有可能瞬间 CPU 飙升到 100%、连接数耗尽,哪怕只是持续 1 秒,也可能导致数据库临时不可用,进而引发依赖该数据库的其他接口连锁故障 —— 这就是缓存击穿:单个热点数据缓存过期瞬间,大量并发请求穿透缓存,集中冲击数据库。
它和缓存穿透的核心区别在于:穿透是查不存在的数据,击穿是查存在但缓存刚好过期的数据;穿透的请求是分散的随机 Key,击穿的请求是集中的同一热点 Key。
2、解决方法。
2.1 互斥锁
既然问题出在大量请求同时查库,那互斥锁的思路就很直接:给查库操作加把锁,让同一时间只有一个请求能去查数据库,其他请求要么等锁、要么直接返回缓存旧值(如果允许的话),从根源上避免数据库被蜂拥而至的请求压垮。

这种方法的适用场景很广,尤其是热点 Key 数量不固定、偶尔会新增的场景 —— 比如电商大促时,突然火起来的网红单品,事前没法确定它是热点,但用互斥锁能临时顶住压力。
优点:不用提前预判热点 Key,通用性强;而且缓存更新是实时的,能最大程度保证缓存与数据库数据一致。缺点:加锁和等待的过程会增加请求的响应时间,如果锁没做好释放,还可能导致死锁,得给锁加个过期时间来规避。
2.1 自动续期
缓存自动续期的核心思路是变被动等待过期为主动提前更新:不再依赖缓存的 TTL(过期时间)被动失效,而是在业务层单独部署定时任务(如基于 XXL-Job、Quartz 等调度框架),按预设频率从数据库拉取热点 Key 的最新数据,主动覆盖缓存中的旧值。这样,用户请求永远命中缓存,没过期窗口期,自然不会发生缓存击穿。

这种方法适合热点 Key 提前明确、数量较少的场景 —— 比如电商首页固定展示的Banner 图(循环轮播的广告图片 / 宣传图)、秒杀商品。这类场景的热点 Key 不会频繁新增或变更,用定时任务维护缓存更新成本低、效果稳定。
优点:实现简单,完全杜绝击穿风险;缺点:只适合热点 Key 明确且数量少的场景,如果热点 Key 太多,定时任务会频繁查询数据库,反而增加数据库压力;且数据更新有延迟,可能出现缓存与数据库不一致的短暂问题。
2.2 逻辑过期
逻辑过期指的是它不让缓存真的过期,也就是不设置 TTL,而是在缓存的 Value 里加一个逻辑过期时间字段,例如:
{
"product_info": {
"product_id": 1001,
"product_name": "智能手机",
"product_price": 2999.00,
"stock_quantity": 867,
"is_seckill": true
},
"logical_expire_time": "2024-05-20 02:00:00"
}
具体流程是这样的:
用户请求查商品 1001,先读缓存,发现缓存存在;检查缓存里的逻辑过期时间:如果没过期,直接返回商品信息;如果逻辑过期了,不立刻删除缓存,而是启动一个异步线程(比如用线程池)去数据库查最新数据、更新缓存(包括更新逻辑过期时间);与此同时,当前请求不等待异步线程的结果,直接返回缓存里的旧数据。

这种方法的核心是用旧数据换并发安全—— 即使缓存逻辑过期了,也先给用户返回旧数据,再偷偷异步更新缓存,既避免了请求涌向数据库,又保证了用户体验(不用等)。
适合什么场景?对数据实时性要求不高,但对并发和响应速度要求高的场景 —— 比如电商商品的浏览量、收藏数、新闻资讯的列表页。
优点:并发能力极强,没有锁等待,响应速度快;缺点:会返回旧数据,不适合实时性要求高的场景(比如秒杀商品的库存,旧数据可能导致超卖);而且缓存永远不删,会占用更多内存,需要定期清理无效缓存。
3、总结
缓存击穿的本质是热点数据过期瞬间的并发查库,解决思路围绕避免大量请求同时查库展开,不同方案各有适配场景,实际开发中要按需选择:
- 若热点 Key 提前明确、数量少 → 选「自动续期」,简单高效,零击穿风险;
- 若热点 Key 不固定、偶尔新增,且要求数据实时性 → 选「互斥锁」,通用性强,数据一致;
- 若对并发和响应速度要求高,对数据实时性要求低 → 选「逻辑过期」,并发拉满,用户体验好。
实际项目中不用局限于单一方案,可以把互斥锁和逻辑过期结合 —— 逻辑过期时,先返回旧数据,再用互斥锁保证只有一个异步线程去查库,既避免并发查库,又防止多个异步线程重复更新缓存,性价比更高。

3658

被折叠的 条评论
为什么被折叠?



