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

用Redis算过期时间那点事儿,怎么高效又靠谱地搞定过期计算

说到用Redis处理过期时间,这事儿听起来简单,不就是给数据设个存活时间嘛,但真想做得既高效又不出岔子,里头还真有不少细节要注意,咱们今天就不绕弯子,直接聊聊怎么把它搞定。

核心就两点:别让不该在的数据留着,也别误删了还在用的数据。

最基础也是最常用的方法,就是Redis自带的过期键功能,你往Redis里塞一个键值对的时候,顺手用EXPIRE命令或者直接在设置命令里带上过期参数(比如SET key value EX seconds),告诉Redis这个键多久以后该自动消失,Redis自己会在后台悄悄地检查并删除那些过期的键,这个方法的好处是省心,你几乎不用管,Redis帮你打理得井井有条。(来源:Redis官方文档关于键过期的说明)

这个“省心”是有条件的,Redis删除过期键主要有两种策略:惰性删除定期删除

  • 惰性删除的意思是:只有当某个客户端尝试去访问一个键的时候,Redis才会顺带检查一下这个键是不是过期了,如果过期了就当场删除,然后告诉客户端“这个键不存在”,这就像你家里有个储物箱,只有当你需要找东西打开箱子时,才会把里面的过期食品扔掉,这种方式对CPU很友好,不会浪费资源去检查没人用的键,但坏处是,如果某个过期的键一直没人访问,它就会一直占着内存不释放,成了“僵尸键”。(来源:Redis官方文档对惰性删除的解释)

  • 为了弥补惰性删除的不足,Redis还有定期删除机制,它会每隔一段时间,随机抽取一部分设置了过期时间的键,检查它们是否过期,如果过期就删除,如果这次抽查发现过期键的比例很高,它就会继续抽检更多,直到比例降下来,这就像物业会定期巡查小区公共区域,发现垃圾就清理,这种方式可以清理掉那些“僵尸键”,但如果设置不当,抽查得太频繁可能会对性能产生一点影响。(来源:Redis官方文档对定期删除的解释)

光靠Redis自己清理,在数据量巨大或者过期键非常多的情况下,可能还是会有些许延迟,或者内存释放不够及时,这时候,如果你想更“主动”一点,确保数据按时失效,就得自己想办法了。

用Redis算过期时间那点事儿,怎么高效又靠谱地搞定过期计算

一个常见的进阶玩法是使用 Sorted Set(有序集合),思路是这样的:你把需要过期的数据本身作为Sorted Set里的成员(member),而把它的过期时间戳(比如2023-10-27 15:30:00对应的时间戳)作为这个成员的分数(score),你可以单独启动一个后台任务,或者使用定时器,定期执行ZRANGEBYSCORE命令,查询分数(即过期时间戳)小于当前时间戳的所有成员,这些成员就是已经过期的数据,查出来之后,你先进行必要的逻辑处理(比如记录日志、更新其他相关数据),然后再把它们从Sorted Set里删除,同时别忘了删除这些数据本身在主键空间里对应的键。

这个方法的好处是,你把过期时间的控制权牢牢抓在了自己手里,过期检查的时机和频率你自己定,非常精准和及时,很多需要做延时任务(比如下单30分钟未支付自动关闭)的场景,其实就是用的这个原理,但缺点也很明显,你需要自己写这个轮询的代码,增加了系统的复杂性,而且这个轮询任务本身也会消耗资源。(来源:常见的Redis延时任务实现方案)

那有没有更省力又相对可靠的办法呢?有,可以结合使用Redis的发布订阅(Pub/Sub) 功能,不过这里要注意,Redis的键空间通知(Keyspace Notifications)功能虽然能在键被删除(包括过期删除)时发布一个事件,但你得先配置开启这个功能,开启后,你可以订阅特定的频道,当有键过期时,Redis会发布一条消息到这个频道,你的应用接收到消息后就可以执行相应的清理逻辑。

这里有个非常重要的坑需要注意: Redis的过期删除事件通知是不可靠的,意思是,它只在键确实被访问后触发惰性删除,或者被定期删除任务扫描到并删除时,才会发出通知,它不能保证每一个过期的键都会触发通知,如果你的应用依赖于接收每一个过期事件来做关键业务逻辑(比如释放重要资源),那可能会出问题,所以这个机制通常更适合用于一些辅助性的、丢了几个通知也无伤大雅的场景,比如清理本地缓存、更新统计信息等。(来源:Redis官方文档对键空间通知可靠性的说明)

用Redis算过期时间那点事儿,怎么高效又靠谱地搞定过期计算

总结一下,怎么高效又靠谱地搞定Redis的过期计算?

  1. 对于绝大多数常规场景:放心使用Redis内置的EXPIRE命令,它的惰性删除加定期删除组合拳已经能处理大部分情况,简单高效,你需要做的就是根据业务需求合理设置过期时间,并监控内存使用情况。

  2. 对于要求过期非常准时、不能有延迟的业务(比如优惠券秒杀、抢购库存释放):推荐使用Sorted Set自己维护一个过期队列,虽然麻烦点,但精准可控,最靠谱。

  3. 对于希望得到过期回调,但可以容忍极少数事件丢失的辅助性场景:可以开启Redis的键空间通知功能来订阅过期事件,作为补充手段。

无论用哪种方法,关键是要理解其背后的原理和局限性,根据你自己业务的“靠谱”级别要求来选择,别为了追求极致的可靠性而过度设计,也不要因为贪图省心而忽略了关键业务可能存在的风险,测试和监控永远是保证系统稳定性的不二法门。