SET key value EX 10 这条命令在 Redis 服务端内部的全过程。
整个流程可以分为三大阶段:命令接收与处理、数据存储与过期设置 以及 过期键的删除。
阶段一:命令接收、解析与执行
当 Redis 服务端通过网络连接接收到来自客户端的 SET key value EX 10 命令时,它会经历以下步骤:
读取命令:Redis 的事件循环机制(Event Loop)监听到客户端套接字(socket)变为可读状态,于是从该套接字读取传入的命令字符串。
命令解析:服务端会解析收到的原始命令字符串。
SET key value EX 10会被解析成一个包含多个参数的命令请求,类似于一个数组:["SET", "key", "value", "EX", "10"]。命令查找:Redis 会在一个命令表中查找
SET这个命令。这个表是一个哈希表,存储了所有 Redis 命令的名称以及指向其实现函数的指针。参数校验与选项处理:找到
SET命令的实现函数后,程序会开始校验参数。- 它会检查参数数量是否正确。
- 接着,它会解析
EX 10这个选项。EX告诉 Redis 需要设置一个以秒为单位的过期时间。程序会读取10,并将其理解为“10秒”。 - 在内部,
SET命令的EX选项实际上是SETEX命令的一个语法糖,最终都会调用相似的核心逻辑。
计算绝对过期时间:Redis 不会 存储相对的 TTL(Time-To-Live,生存时间)值,比如“10秒”。相反,它会将这个相对时间转换成一个绝对的未来的Unix时间戳(从 Redis 2.6 开始,精度为毫秒)。. 计算方式大致为:
当前服务器的Unix时间戳(毫秒) + 10 * 1000。这个绝对时间戳是后续判断键是否过期的唯一依据。
阶段二:数据存储与过期信息关联
计算出绝对过期时间后,Redis 会执行核心的写操作:
创建或更新键值对:
- Redis 会在主数据库字典(一个全局哈希表)中查找指定的
key。 - 如果
key已经存在,它会更新该key对应的value,并丢弃任何先前与该key关联的过期时间。 - 如果
key不存在,它会创建一个新的键值对条目。
- Redis 会在主数据库字典(一个全局哈希表)中查找指定的
存储过期时间:最关键的一步是,Redis 会将上一步计算出的绝对过期时间戳存储起来。这个信息存储在一个独立的、专门用于管理过期键的字典(
expires字典)中。- 键:指向主数据库中
key对象的指针。 - 值:一个
long long类型的整数,即那个绝对的毫秒级 Unix 时间戳。
- 键:指向主数据库中
通过这种方式,Redis 将键值数据和它的过期信息分离开来,使得查找键值和检查过期可以高效地进行。
- 返回响应:一旦数据和过期时间都成功设置,服务端会向客户端返回一个简单的字符串响应
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毫秒一次)。 - 扫描策略:定期删除并不是遍历所有设置了过期时间的键,因为这在键数量庞大时会阻塞服务器。它采用了一种随机抽样的策略:
- 从设置了过期时间的键集合中随机抽取 N 个键(默认为20个)。
- 检查这 N 个键是否过期,并将所有已过期的键删除。
- 如果被删除的键的比例超过某个阈值(例如25%),则立即重复执行一轮抽样和删除操作,直到被删除键的比例低于该阈值,或者达到了单次处理的时间上限,以避免长时间阻塞主线程。
优点:通过限制单次执行的时长和频率,有效控制了对CPU时间的占用,同时也能清理掉那些从未被访问的过期键,释放内存。 缺点:这是一个基于概率和采样的算法,因此无法保证所有过期键都能被准时删除。可能会有少量过期键的删除存在延迟。
总结
SET key value EX 10 命令的生命周期如下:
- 客户端请求:客户端发送命令。
- 服务端处理:服务器解析命令,将
10秒转换为未来的一个绝对Unix时间戳。 - 数据存储:将
key-value存储在主数据区,并将key和其绝对过期时间戳存入一个专门的过期字典。 - 过期等待:在接下来的10秒内,该键可以被正常访问。
- 过期删除:10秒后,该键会因为以下两种情况之一被删除:
- 惰性删除:客户端下一次尝试访问它时,被发现已过期并被删除。
- 定期删除:后台的定期删除任务在随机抽样中“幸运地”选中了它,发现其已过期并删除。
这个高效、务实的组合策略,确保了 Redis 在高性能和有效内存管理之间取得了出色的平衡。