Redis - 过期删除机制

一、过期时间

1. 设置命令

EXPIRE <KEY> <TTL> : 将键的生存时间设为 ttl 秒
PEXPIRE <KEY> <TTL> :将键的生存时间设为 ttl 毫秒
EXPIREAT <KEY> <timestamp> :将键的过期时间设为 timestamp 所指定的秒数时间戳
PEXPIREAT <KEY> <timestamp>: 将键的过期时间设为 timestamp 所指定的毫秒数时间戳

2. 数据结构

redisDb 结构中,expires 字典保存了所有键的过期时间:

typedef struct redisDb {
    ...
    dict *dict;     // 数据库键空间,保存着数据库中所有键值对
    dict *expires   // 过期字典,保存着键的过期时间
    ...
} redisDb;

二、过期键删除策略

Redis 中有三种过期键删除策略:

  • 定时删除:在设置键的过期时间的同时,创建一个定时器,让定时器在到达过期时间时,立即执行删除操作(CPU不友好、内存友好)
  • 惰性删除:每次获取键时,才判断是否过期,如果过期就删除;没有过期则返回(CPU友好、内存不友好)。
  • 定期删除:每隔一段时间,就对 Redis 进行一遍检查,删除里面过期的键(折中)。

Redis 默认使用惰性删除与定期删除相结合,配合使用。

三、淘汰策略

思考:如果所有的 Key 都没有设置过期时间,但内存满了怎么办?

maxmemory 可以设置最大使用内存,64 位系统中如果不设置可以使用所有的内存。

此外,Redis 提供了几种淘汰策略:

  • volatile-lru:从已设置过期时间的数据集中挑选最近最少使用的数据淘汰
  • volatile-ttl:从已设置过期时间的数据集中挑选将要过期的数据淘汰
  • volatile-random:从已设置过期时间的数据集中任意选择数据淘汰
  • allkeys-lru:从数据集中挑选最近最少使用的数据淘汰
  • allkeys-random:从数据集中任意选择数据淘汰
  • no-enviction:禁止淘汰数据,如果内存满了就只能接受读请求

近似 LRU 算法

LRU 算法需要一个双向链表来记录数据的最近被访问顺序,但是出于节省内存的考虑,Redis 的 LRU 算法并非完整实现。Redis 的 LRU 算法并非完整的实现。Redis 并不会选择最久未被访问的键进行回收,相反它会尝试运行一个近似 LRU 的算法,通过对少量键进行取样,然后回收其中的最久未被访问的键。通过调整每次回收时的采样数量 maxmemory-samples,可以实现调整算法的精度。