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

Redis过期订阅到底怎么从无限变有限,背后原理和影响聊聊

关于Redis的过期订阅从“无限”到“有限”的转变,这其实是Redis在版本演进中一个非常关键且实用的改进,要理解这个变化,我们得先搞清楚“过期订阅”是什么,以及它最初为什么会被认为是“无限”的。

什么是Redis的过期订阅?

就是当Redis中一个带过期时间的key(比如一个验证码缓存,设置了5分钟过期)真正被删除的那一刻,Redis可以主动通知那些“订阅”了这个事件的客户端,客户端不需要不停地去轮询检查key还在不在,而是像听广播一样,等到事件发生就行了,这种机制在很多场景下非常有用,比如订单超时未支付自动关闭、锁超时释放后的清理动作等。

最初的“无限”模式及其问题

在Redis早起的版本中(具体是2.8版本之前),这个通知机制是通过一种叫做“被动过期”的方式触发的,这里的“无限”并不是指通知数量没有限制,而是指触发通知的时机是“无限”可能的

它的工作原理是这样的(根据Redis官方文档对早期行为的描述):

Redis过期订阅到底怎么从无限变有限,背后原理和影响聊聊

  1. 你给一个key设置了过期时间,setex mykey 10 "hello"
  2. Redis并不会主动、准时地在10秒后去删除这个key,相反,它采取了一种“惰性”策略。
  3. 触发点1(惰性删除):当某个客户端尝试来读取这个mykey时,Redis会先检查一下它是否已经过期,如果过期了,就立刻删除它,然后才返回这个key不存在的信息,在删除的这个动作发生时,才会顺带产生一个过期事件通知给订阅者。
  4. 触发点2(定期抽样删除):为了避免大量过期key永远没人访问而导致内存泄漏,Redis也会定期(每秒10次)随机抽取一部分带过期时间的key进行检查并删除过期的,同样,在抽样中删除的key也会触发通知。

这就导致了所谓的“无限”不确定性,一个key的实际删除时间,可能是在它过期后的瞬间(如果运气好被定期抽样抽中了),也可能是在它过期后很久很久(比如一个小时之后),直到第一个客户端来访问它的时候才被删除,订阅者收到通知的时间点是无法预测的,延迟可能非常大,这对于需要精确超时控制的业务来说,是不可接受的。

向“有限”延迟的演变:expired-keys事件通道的引入

为了解决这个不确定性的问题,Redis从8版本开始引入了一个重要的改进(根据Antirez的博客和Redis发布说明),这个改进的核心是增加了一个新的、更可靠的事件通道。

Redis过期订阅到底怎么从无限变有限,背后原理和影响聊聊

在2.8版本之后,Redis提供了两种过期事件的通知方式:

  1. __keyevent@<db>__:expired:这是我们上面说的传统方式,通知时机不确定,延迟可能很大。
  2. __keyspace@<db>__:expired这就是实现“有限”延迟的关键

新的__keyspace@<db>__:expired通道的工作机制有了本质变化(根据Redis官方文档对keyspace通知的解释): 它不再仅仅依赖“被动删除”和“定期抽样”来触发通知,Redis内部实现了一个更积极的时间事件处理器,这个处理器会尽可能地在一个key的精确过期时间点到达时,就立即发布一个过期事件到keyspace通道。

这意味着,订阅了keyspace通道的客户端,几乎可以在key过期的那一刻就收到通知,这个延迟被限制在了一个非常小的、可接受的范围内(通常是毫秒级),从之前的“无限可能”变成了“基本确定”,我们说过期订阅从“无限”延迟变成了“有限”延迟。

这个变化带来的影响

  1. 可靠性提升:这是最直接的影响,业务方现在可以信赖Redis的过期事件来做一些实时性要求高的操作,秒杀”活动中订单持有时间的精确控制,或者分布式锁的自动释放和续期判断,不再需要自己实现一套复杂的轮询检查机制,简化了架构。
  2. 性能考量:更积极的过期检查意味着CPU开销会略有增加,因为Redis需要在更精确的时间点去处理这些过期事件,虽然Redis做了优化(比如使用无序集合“时间轮”来高效管理大量过期时间),但在设置海量、且过期时间高度集中的key时,仍需关注对性能的潜在影响。
  3. 开发者需要做的改变:开发者在使用过期订阅功能时,必须明确知道自己订阅的是哪个通道,如果你需要低延迟的精确通知,就应该订阅__keyspace@0__:expired(以0号数据库为例),如果订阅错了通道,可能还会觉得这个功能“不靠谱”。
  4. 功能上的补充而非替代:需要注意的是,新的keyspace通道并没有完全取代旧的keyevent通道,它们是并存的,通知的内容格式也略有不同(keyspace通知key的名字,keyevent通知触发的事件类型),旧的通道由于其触发机制,在某些特定场景下(比如不关心延迟,只关心最终一致性)可能仍然有用。

总结一下: Redis通过引入keyspace过期事件通道,将过期通知从依赖随机和被动触发的“无限”延迟模式,升级到了主动在精确时间点触发的“有限”延迟模式,这个改进极大地增强了该功能的实用性和可靠性,使其成为构建实时响应系统的一个有力工具,是Redis走向更成熟、更可靠的数据系统的重要一步。