当前位置:首页 > 问答 > 正文

聊聊Redis里怎么搞个随机过期时间,避免缓存雪崩那点事

聊聊Redis里怎么搞个随机过期时间,避免缓存雪崩那点事

咱们先来聊聊缓存雪崩是个啥,你可以想象一下这个场景:双十一刚过,你开了一家卖猫粮的网店,为了让大家查订单快一点,你把很多订单信息都放在了Redis这个缓存里,你图省事,给所有这些缓存数据都设置了完全一样的过期时间,比如都是1小时后失效。

结果呢,1小时“铛”的一声到了,这一大波缓存数据就像商量好了一样,齐刷刷地全部失效了,这时候,下一个来查订单的用户,发现缓存里没数据了(因为刚好全失效了嘛),他的请求就会直接撞到后端的数据库上,数据库正哼哧哼哧地准备处理这一个请求,紧接着,成千上万个用户的请求像海啸一样涌了过来——因为他们也查不到缓存了,数据库哪见过这阵仗啊,CPU瞬间飙到100%,连接数被占满,很快就扛不住压力“趴窝”了,这就是缓存雪崩:大量缓存数据在同一时间点失效,导致所有请求都砸向数据库,最终把数据库打垮,整个服务不可用。

那怎么避免这种“集体罢工”的惨剧呢?一个非常有效又简单的办法就是:别让它们在同一时间点失效,给它们设置随机的过期时间。

为啥随机过期时间能解决问题?

道理很简单,就像我们平时上班错峰出行一样,如果所有人都要求早上9点整准时到公司,那地铁站肯定爆满,电梯也挤不上去,但如果大家的上班时间有的8点半,有的9点,有的9点15,压力就被平均分散开了。

同理,我们给缓存数据设置过期时间时,不要用一个固定的数值,比如3600秒(1小时),而是在这个基础值上,加上一个随机的“抖动值”,比如说,我们可以把过期时间设置为:基础过期时间 + 一个在正负一定范围内的随机数

举个例子:

  • 基础过期时间我们还是设为1小时(3600秒)。
  • 随机范围我们可以设定为正负5分钟(300秒)。
  • 每个缓存数据实际的过期时间就会是在 3300秒(55分钟)到 3900秒(65分钟) 之间的一个随机值。

这样一来,原本应该在同一时刻(比如10:00)集体过期的缓存,现在会分散在9:55到10:05这10分钟的时间窗口内陆续失效,数据库在每个时间点需要处理的请求数量就大大减少了,从“海啸”变成了“波浪”,完全在它的承受范围之内,服务也就平稳了。

具体在代码里怎么实现这个“随机”呢?

这个方法非常简单,几乎不增加任何额外的开发成本,在你写代码往Redis里设置缓存的时候,本来你会写类似 set key value EX 3600 这样的命令,意思是过期时间3600秒,你只需要把这3600秒变成一个随机数就行了。

用伪代码表示就是:

基础过期时间 = 3600  # 1小时
随机偏移量 = 生成一个 -300 到 +300 之间的随机数  # 正负5分钟
实际过期时间 = 基础过期时间 + 随机偏移量
redis.set(key, value, 过期时间=实际过期时间)

注意,这个随机偏移量可以是正数也可以是负数,这样才能真正实现“错峰”,如果只加正数,那只是延后了雪崩的发生时间,并没有分散开。

除了随机过期,还有哪些招数可以配合使用?

虽然随机过期时间是预防雪崩的一剂良药,但为了确保系统更健壮,我们通常还会配合其他一些方法:

  1. 永不过期 + 后台更新:这是更高级一点的策略,我们给缓存设置成永不过期,然后启动一个后台任务(比如定时任务或者一个独立的进程),定期去刷新缓存的数据,这样根本就不会有“过期”这一说,自然也就没有雪崩了,不过这个方案复杂一些,要自己管理更新的逻辑。

  2. 加个锁或者队列:即使发生了小范围的缓存失效,比如某个热门key刚好过期了,这时候突然来了很多请求,我们可以让第一个请求去数据库查数据并回填缓存,后面的请求先等着(比如用个锁),或者排个队,等缓存回填好了再去读缓存,这样可以避免多个请求同时去查数据库,但这会影响一些性能,属于一种保护机制。

  3. 给数据库也建个保护罩:这是最后一道防线,我们可以对数据库做一些保护措施,比如使用熔断器模式,当检测到数据库压力过大、响应过慢或错误增多时,系统会自动“熔断”,短时间内直接拒绝掉一部分请求,给出一个友好的提示(如“系统繁忙,请稍后再试”),从而保证数据库不会彻底崩溃,等它恢复过来再正常服务,这叫做“弃车保帅”。

总结一下

避免缓存雪崩的核心思想就两个字:分散,而设置随机过期时间,是实现“分散”最简单、最直接、最有效的方法之一,它就像给缓存失效这个事件安装了一个“错峰器”,把巨大的冲击力化解为一段段时间内的小波动,让数据库能够从容应对。

下次你在项目里用Redis设置缓存过期时间时,如果数据量比较大,千万别再偷懒写死一个固定值了,多花一行代码,生成一个带随机偏移量的过期时间,就能为你的系统避免一场潜在的“雪崩”灾难,这笔“投资”简直太划算了。

聊聊Redis里怎么搞个随机过期时间,避免缓存雪崩那点事