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

Redis超时时间到底是咋算的?那些细节和原理其实挺复杂的,得好好琢磨下

关于Redis的超时时间是怎么算的,这事儿确实不像表面看起来那么简单,里面有不少门道,咱们就按照一个普通人使用Redis时可能会碰到的场景,一步步把它琢磨透。

核心概念:过期键是怎么被管理的?

你得知道,当你给一个Redis的键(key)设置了过期时间(SET mykey "hello" EX 60,表示60秒后过期),Redis并不会为每一个键都启动一个单独的计时器,那样的话,如果有几百万个键要过期,光计时器就把系统资源耗光了,那它咋办呢?Redis用了一种更聪明也更高效的办法,结合了两种策略:惰性删除定期删除

第一种策略:惰性删除——等用到的时候再检查

这个名字听起来有点懒,但很实用,意思是,Redis不会主动去检查所有键有没有过期,而是当你去访问这个键的时候(比如用 GET mykey),它才会顺带检查一下这个键是否已经过期了,如果发现过期了,那好,当场就把这个键删除掉,然后返回一个空值给你,就像这个键从来不存在一样。

Redis超时时间到底是咋算的?那些细节和原理其实挺复杂的,得好好琢磨下

这种策略的好处是省力气,只在实际可能产生影响的时候才干活,但坏处也很明显:如果一个键已经过期了,但一直没人来访问它,那它就会一直赖在内存里,成了所谓的“僵尸键”,白占着地方,这显然不行,所以需要第二种策略来帮忙。

第二种策略:定期删除——主动巡逻清理

为了清理那些没人访问的过期键,Redis会每隔一段时间(默认是每秒10次,也就是100毫秒一次)就主动地随机抽取一部分设置了过期时间的键进行检查,这个巡逻清理的过程,可以理解为Redis开了一个小差,快速地进行以下步骤:

  1. 随机抽检:从所有设置了过期时间的键中,随机抽出一定数量(比如20个)的键。
  2. 检查过期:挨个检查这20个键,看它们是否已经过期。
  3. 清理门户:把其中所有过期的键都删除掉。
  4. 判断是否继续:如果在这批抽检的键中,发现有过期键的比例超过了25%,那就说明内存中过期的键还挺多的,这时候Redis会觉得“这次没打扫干净”,它会立刻再随机抽20个键,重复上面的检查删除过程,直到某一次抽检中过期键的比例低于25%,它才认为“差不多干净了”,然后这次巡逻就结束。

这种定期删除的策略,相当于一个辅助的垃圾回收机制,专门对付那些惰性删除照顾不到的“死角”,通过控制巡逻的频率和每次清理的“力度”(比如过期键比例超过25%就继续清理),Redis在CPU开销和内存释放之间取得了一个很好的平衡。

Redis超时时间到底是咋算的?那些细节和原理其实挺复杂的,得好好琢磨下

超时时间是怎么“算”的?——底层存储的奥秘

现在回到最核心的问题:Redis是怎么知道一个键什么时候过期的?它到底是怎么计算和存储这个时间点的?

根据《Redis设计与实现》这本书里的说明,Redis内部实际上维护了一个叫做过期字典的结构,你可以把它想象成一个巨大的表格:

  • 键名:就是你的那个key。
  • :就是这个key的过期时间戳,一个精确到毫秒的Unix时间戳。

举个例子,假设你在北京时间2024年5月27日10:00:00(换算成Unix毫秒时间戳可能是1716861600000)执行了 SET mykey "hello" EX 60,那么Redis在过期字典里就会记录: 键名:mykey -> 过期时间戳:1716861600000 + 60000 = 1716861660000。

Redis超时时间到底是咋算的?那些细节和原理其实挺复杂的,得好好琢磨下

之后,无论是惰性删除还是定期删除,当需要判断 mykey 是否过期时,Redis根本不需要去“计算”已经过了多少秒,它只需要做一件非常简单快速的事情:拿当前系统的时间戳(比如现在是1716861661000)和过期字典里记录的那个时间戳(1716861660000)比较一下,如果当前时间戳大于过期时间戳,那就说明这个键已经过期了。

Redis的超时计算,本质上是基于时间戳的比对,而不是基于一个倒计时器,这种方式非常高效和准确。

一些容易踩坑的细节

理解了原理,就能明白一些看似奇怪的现象了:

  1. 时间精度问题:你设置的是秒级精度(比如EX 60),但Redis内部是用毫秒级时间戳存储的,在过期处理上,惰性删除能保证精度,但定期删除是每秒10次,所以一个键的实际过期时间可能会有最多100毫秒的微小延迟,但这在绝大多数场景下完全可以忽略不计。
  2. 命令的副作用:有些命令的执行会触发惰性删除,比如你对一个集合(Set)执行 SCARD(查看元素数量)命令,Redis会先检查这个集合key是否过期,如果过期了就删除它,然后告诉你这个集合不存在,元素数量是0。
  3. 持久化时的处理:当Redis需要把数据持久化到硬盘(RDB快照或AOF日志)时,它会对所有键进行检查,过期的键不会被保存,从硬盘恢复数据时,也会过滤掉过期的键。
  4. 主从复制:在Redis主从架构中,过期键的删除是由主节点(Master)控制的,主节点在删除一个过期键后,会模拟向从节点(Slave)发送一个对这个键的删除命令(DEL),从而保证主从数据的一致性,从节点自己不会主动删除过期键,以确保一切以主节点为准。

Redis的超时机制是一个精心设计的混合系统:它用过期字典精确记录每个键的“死亡时间”,用惰性删除保证访问时的即时性,再用定期删除进行后台清扫,从而以极小的系统开销,实现了海量数据的高效生命周期管理,琢磨透这些,你就能更好地理解和使用Redis的过期功能,避免一些潜在的坑。