Skip to content

SET key value EX 10 这条命令在 Redis 服务端内部的全过程。

整个流程可以分为三大阶段:命令接收与处理数据存储与过期设置 以及 过期键的删除

阶段一:命令接收、解析与执行

当 Redis 服务端通过网络连接接收到来自客户端的 SET key value EX 10 命令时,它会经历以下步骤:

  1. 读取命令:Redis 的事件循环机制(Event Loop)监听到客户端套接字(socket)变为可读状态,于是从该套接字读取传入的命令字符串。

  2. 命令解析:服务端会解析收到的原始命令字符串。SET key value EX 10 会被解析成一个包含多个参数的命令请求,类似于一个数组:["SET", "key", "value", "EX", "10"]

  3. 命令查找:Redis 会在一个命令表中查找 SET 这个命令。这个表是一个哈希表,存储了所有 Redis 命令的名称以及指向其实现函数的指针。

  4. 参数校验与选项处理:找到 SET 命令的实现函数后,程序会开始校验参数。

    • 它会检查参数数量是否正确。
    • 接着,它会解析 EX 10 这个选项。EX 告诉 Redis 需要设置一个以秒为单位的过期时间。程序会读取 10,并将其理解为“10秒”。
    • 在内部,SET 命令的 EX 选项实际上是 SETEX 命令的一个语法糖,最终都会调用相似的核心逻辑。
  5. 计算绝对过期时间:Redis 不会 存储相对的 TTL(Time-To-Live,生存时间)值,比如“10秒”。相反,它会将这个相对时间转换成一个绝对的未来的Unix时间戳(从 Redis 2.6 开始,精度为毫秒)。. 计算方式大致为:当前服务器的Unix时间戳(毫秒) + 10 * 1000。这个绝对时间戳是后续判断键是否过期的唯一依据。

阶段二:数据存储与过期信息关联

计算出绝对过期时间后,Redis 会执行核心的写操作:

  1. 创建或更新键值对

    • Redis 会在主数据库字典(一个全局哈希表)中查找指定的 key
    • 如果 key 已经存在,它会更新该 key 对应的 value,并丢弃任何先前与该 key 关联的过期时间。
    • 如果 key 不存在,它会创建一个新的键值对条目。
  2. 存储过期时间:最关键的一步是,Redis 会将上一步计算出的绝对过期时间戳存储起来。这个信息存储在一个独立的、专门用于管理过期键的字典(expires 字典)中。

    • :指向主数据库中 key 对象的指针。
    • :一个 long long 类型的整数,即那个绝对的毫秒级 Unix 时间戳。

通过这种方式,Redis 将键值数据和它的过期信息分离开来,使得查找键值和检查过期可以高效地进行。

  1. 返回响应:一旦数据和过期时间都成功设置,服务端会向客户端返回一个简单的字符串响应 OK,表示命令执行成功。

阶段三:过期键的处理与删除机制

现在,key 已经被设置了过期时间。但 Redis 是如何发现并删除这个 key 的呢?它并不会为每个键都创建一个定时器,因为那样会极大地消耗CPU资源。相反,Redis 采用了一种结合了惰性删除定期删除的策略。

1. 惰性删除(Passive Expiration)

这是一种被动的删除方式。当客户端尝试访问一个带有过期时间的键时(例如,执行 GET key),Redis 会在执行命令前先检查这个键是否过期。

  • 检查机制:Redis 会获取当前服务器的Unix时间戳,并与存储在该键上的绝对过期时间戳进行比较。
  • 处理结果
    • 如果 当前时间戳 >= 过期时间戳,说明键已经过期。Redis 会立即删除这个键,然后向客户端返回 nil(空),就好像这个键从未存在过一样。
    • 如果 当前时间戳 < 过期时间戳,说明键未过期。Redis 会正常执行命令(比如返回 value)。

优点:对CPU友好,只有在访问键时才进行检查,不会浪费计算资源在从未被访问的过期键上。 缺点:如果一个键设置了过期时间但之后再也没有被访问,它会一直占用内存,直到被定期删除策略清理掉。

2. 定期删除(Active Expiration)

为了弥补惰性删除的不足,Redis 会在后台定期地、主动地扫描并删除已过期的键。

  • 执行频率:Redis 的服务器周期性操作函数 serverCron 默认每秒执行10次(即每100毫秒一次)。
  • 扫描策略:定期删除并不是遍历所有设置了过期时间的键,因为这在键数量庞大时会阻塞服务器。它采用了一种随机抽样的策略:
    1. 从设置了过期时间的键集合中随机抽取 N 个键(默认为20个)。
    2. 检查这 N 个键是否过期,并将所有已过期的键删除。
    3. 如果被删除的键的比例超过某个阈值(例如25%),则立即重复执行一轮抽样和删除操作,直到被删除键的比例低于该阈值,或者达到了单次处理的时间上限,以避免长时间阻塞主线程。

优点:通过限制单次执行的时长和频率,有效控制了对CPU时间的占用,同时也能清理掉那些从未被访问的过期键,释放内存。 缺点:这是一个基于概率和采样的算法,因此无法保证所有过期键都能被准时删除。可能会有少量过期键的删除存在延迟。

总结

SET key value EX 10 命令的生命周期如下:

  1. 客户端请求:客户端发送命令。
  2. 服务端处理:服务器解析命令,将 10 秒转换为未来的一个绝对Unix时间戳。
  3. 数据存储:将 key-value 存储在主数据区,并将 key 和其绝对过期时间戳存入一个专门的过期字典。
  4. 过期等待:在接下来的10秒内,该键可以被正常访问。
  5. 过期删除:10秒后,该键会因为以下两种情况之一被删除:
    • 惰性删除:客户端下一次尝试访问它时,被发现已过期并被删除。
    • 定期删除:后台的定期删除任务在随机抽样中“幸运地”选中了它,发现其已过期并删除。

这个高效、务实的组合策略,确保了 Redis 在高性能和有效内存管理之间取得了出色的平衡。

Released under the MIT License.