Redis 可以对 key 设置过期时间,过期删除策略:将已过期的键值对删除
设置过期
设置 key 过期时间的命令一共有 4 个:
expire <key> <n>
:key 在 n 秒后过期pexpire <key> <n>
:key 在 n 毫秒后过期expireat <key> <n>
:key 在指定时间戳后过期,精确到秒:10 位时间戳pexpireat <key> <n>
:key 在指定时间戳后过期,精确到毫秒:13 位时间戳
设置字符串,同时对 key 设置过期时间,共有 3 种命令:
set <key> <value> ex <n>
:设置键值对,同时指定 n 秒后过期set <key> <value> px <n>
:设置键值对,同时指定 n 毫秒后过期setex <key> <n> <value>
:设置键值对,同时指定 n 秒后过期
其他:
TTL <key>
:查看某个 key 剩余存活时间,单位为秒PTTL <key>
:查看某个 key 剩余存活时间,单位为毫秒-1
:永不过期-2
:已过期
PERSIST <key>
:取消 key 的过期时间,即:永不过期
如何判定 key 已过期了?
- Redis 使用一个哈希表保存所有键值对数据
- 使用另一个名为过期字典(expires dict)的哈希表保存所有设置了过期时间的 key 和过期时间
typedef struct redisDb {
dict *dict; // 键值对数据
dict *expires; // 键的过期时间
...
} redisDb;
过期字典:
key
是一个指针,指向某个键对象value
是一个long long
类型的整数,表示key
的过期时间(13 位的时间戳,精确到毫秒)
判定 key 是否过期的流程:
graph TB A(客户端) --请求--> B{在过期字典中?} B --是--> C{当前系统时间小于<br />查询 key 的过期时间?} B --否--> D(未过期) C --是--> D C --否--> E(已过期)
过期删除策略有哪些?
常见的三种过期删除策略:
- 定时删除
- 在设置 key 的过期时间时,同时创建一个定时事件,当时间到达时,由事件处理器自动执行 key 的删除操作
- 内存友好;CPU 不友好
- 惰性删除
- 不主动删除过期键,每次访问 key 时,都检测 key 是否过期,如果过期则删除该 key
- 内存不友好;CPU 友好
- 定期删除
- 每隔一段时间「随机」取出一定数量的 key 进行检查,并删除其中的过期 key
- 通过限制删除操作执行的时长和频率,权衡内存和 CPU 的资源占用
- 问题是:难以很好地确定删除操作执行的时长和频率
Redis 选择惰性删除 + 定期删除这两种策略配合使用,以求在合理使用 CPU 时间和避免内存浪费之间取得平衡
Redis 如何实现惰性删除?
在访问或者修改 key 之前,检查 key 是否过期:
- 如果过期,则删除该 key,可以异步删除也可以同步删除(根据
lazyfree_lazy_expire
参数配置决定),然后返回null
- 如果没有过期,不做任何处理,返回正常的键值
Redis 如何实现定期删除?
定期删除:每隔一段时间随机取出一定数量的 key 进行检查,并删除其中的过期 key
间隔检查的时间是多长?
Redis 默认每秒进行 10 次过期检查,可通过配置文件 redis.conf
进行配置:
// redis.conf
hz 10 // 默认每秒进行 10 次过期检查
随机抽查的数量是多少?
源码中,定期删除的实现在 expire.c
文件的 activeExpireCycle
函数中,其中随机抽查的数量由 ACTIVE_EXPIRE_CYCLE_KEYS_PER_LOOP
定义,它是写死在代码中的,数值是 20(源代码)
定期删除的流程:
- 从过期字典中随机抽取 20 个 key
- 检查这 20 个 key 是否过期,并删除已过期的 key
- 如果本轮检查的已过期 key 数量超过 5 个(占比大于 25%),则继续重复步骤 1,否则结束
可以看到,定期删除是一个循环的流程。为了保证不会出现循环过度,导致线程卡死现象,增加了定期删除循环流程的时间上限,默认不会超过 25ms
定期删除流程伪代码:
do {
expired = 0; // 已过期的数量
num = 20; // 随机抽取的数量
while (num--) {
// 1. 从过期字典中随机抽取 1 个 key
// 2. 判断该 key 是否过期,如果已过期则进行删除,同时 expired++
}
// 超过时间限制则退出
if (timelimit_exit) return;
// 如果本轮检查的已过期 key 的数量,超过 25%,则继续随机抽查,否则退出本轮检查
} while (expired > 5);