用Redis怎么挡住缓存穿透这事儿,聊聊那些击穿和防护的小技巧
- 问答
- 2025-12-25 10:01:52
- 3
说到用Redis挡住缓存穿透这事儿,咱们得先掰扯清楚一个前提:缓存是干嘛的?简单说,就是为了减轻数据库的压力,把那些经常被访问的“热乎”数据放在Redis这种读写飞快的内存数据库里,当业务系统要查数据时,先去Redis里找,找不着(这叫缓存未命中)再去数据库找,找到了再塞回Redis,下次就能直接从缓存拿了,这本是个好机制,但如果被恶意或者意外情况利用,就会出问题,缓存穿透和缓存击穿就是两个典型的麻烦。
先聊聊啥是缓存穿透
这事儿说起来挺“气人”的,想象一下,有人(可能是恶意攻击者)不停地用数据库里根本不存在的数据来查询,你的系统用户ID都是从10000开始的,他偏要查用户ID=-1或者ID=9999999999的数据,这时候,你的系统流程是这样的:先查Redis,Redis里肯定没有(因为压根不存在这种数据),然后去查数据库,数据库也白忙活一场,返回空结果,按照正常逻辑,这个空结果通常是不会被存进Redis的(或者只存很短时间)。

那么问题来了,下次同一个恶意请求又来的时候,流程重演一遍:查Redis(还是没有)-> 查数据库(还是没有),这就意味着,这种根本不存在的key的每次请求,都像“穿透”了Redis这层保护垫,直接砸在了脆弱的数据库上,如果这种请求量非常大,比如每秒几万次,数据库很可能就因为压力过大而挂掉,导致整个系统瘫痪,这就是缓存穿透。(来源:普遍认可的缓存问题定义)
那怎么防呢?有几个挺实用的小技巧:
-
对请求参数做校验,把歪门邪道挡在门口。 这是最简单也最有效的一步,比如上面说的用户ID是正数,那在业务逻辑的最开始,就应该对传入的ID进行检查,如果是负数或者明显超出合理范围的数,直接返回错误或空结果,根本别让这种请求去走查缓存和数据库的流程,这就像小区保安,看到形迹可疑的人先盘问一下,不对劲儿不让进。

-
把“空结果”也缓存起来。 既然数据库都说了这东西不存在,那好,我就信你一次,当第一次查询某个不存在的数据导致数据库返回空结果时,我们把这个空结果(比如一个null值,或者一个特殊的标记)也存到Redis里,并给它设置一个相对较短的过期时间,比如1-5分钟,那么在下一次同样的恶意请求来时,Redis里就能找到这个“空结果”,直接返回给客户端,从而保护了数据库,注意,过期时间不能太长,否则万一后来数据库里真的有了这个数据,会因为缓存里的空值而无法及时显示。(来源:常见的缓存穿透解决方案)
-
用布隆过滤器这把“筛子”。 这是个更高级但也更高效的武器,你可以把它理解成一个放在Redis前面的、非常节省内存的“集合”,这个集合不存储完整的数据,而是用某种算法告诉你某个key“一定不存在”或者“可能存在”于数据库中。
- 工作流程是:系统启动时,先把数据库里所有存在的key(比如所有有效的用户ID)都预先加载到布隆过滤器中。
- 当有查询请求来时,先让这个key过一遍布隆过滤器。
- 如果过滤器说“一定不存在”,那好了,直接返回空结果,连Redis和数据库都不用查了,彻底挡住。
- 如果过滤器说“可能存在”,那才放行去继续后面的流程(先查Redis,再查数据库)。 布隆过滤器的好处是内存占用极小,但有个小小的缺点:它有一定的误判率(即可能把不存在的key判为“可能存在”),但绝不会把存在的key判为“不存在”,这意味着它可能会放过一些穿透请求到后续流程,但绝不会误杀一个正常请求,对于防护穿透来说,这个特性是完全可以接受的。(来源:应对大规模数据集的常用防护方案)
说完了穿透,再顺带提一下它的“近亲”——缓存击穿

缓存击穿和穿透有点像,但区别在于,击穿是针对一个存在的、非常热点的key,这个key平时在Redis里待得好好的,但当它突然过期的瞬间,恰好有海量的并发请求同时来访问这个key,这下坏了,大家发现Redis里没了,于是所有这些请求都像听到了发令枪响,齐刷刷地冲向数据库,瞬间的巨大压力可能直接把数据库打垮,就像一个保温杯(Redis)护着一壶开水(数据库),杯子突然被拿开,无数只手(请求)同时伸过去抢着接水,肯定烫伤一片。
防护缓存击穿的小技巧:
-
设置热点数据永不过期。 对于一些极度热门、访问频率极高的数据,可以直接在Redis里设置为永不过期,这样就从根源上避免了同时失效的问题,这需要配套有更新策略,比如在数据更新时,同时主动更新Redis里的数据。
-
加互斥锁。 这是更常见的做法,当发现缓存失效时,不是所有请求都马上去查数据库,而是让这些请求先去“抢一把锁”(这个锁可以用Redis的setnx命令实现),只有一个请求能抢到锁,然后由这个幸运儿去查询数据库,并重建缓存,其他没抢到锁的请求则等待一小段时间,然后重新去缓存里获取数据(此时第一个请求可能已经将数据加载好了),这样就避免了大量请求同时冲击数据库,这就像一群人都想进一个房间,但只发一把钥匙,谁抢到谁进去拿东西,其他人等着他拿出来分。(来源:处理热点key并发读的典型方案)
缓存穿透是查不存在的数据,防护关键在于判断并缓存空值,或者用布隆过滤器提前拦截,缓存击穿是热点key失效瞬间的并发冲击,防护关键在于永不过期或加锁重建,把这些小技巧用好了,你的Redis缓存层就能更坚挺,数据库也能更安稳。
本文由称怜于2025-12-25发表在笙亿网络策划,如有疑问,请联系我们。
本文链接:https://haoid.cn/wenda/68100.html
