Redis过期删除策略
悲观者从机会中看到困难。乐观者从困难中看到机会。
——温斯顿·丘吉尔
如何设置过期时间
- 先说一下对 key 设置过期时间的命令。 设置 key 过期时间的命令一共有 4个:
expire <key> <n>: 设置 key 在n秒后过期,比如 expire key 100 表示设置 key 在 100 秒后过期; pexpire <key> <n>:设置 key 在n毫秒后过期,比如 pexpire key2 100000 表示设置 key2 在100000 毫秒(100 秒)后过期。expireat <key> <n>:设置 key 在某个时间戳(精确到秒)之后过期,比如 expireat key3 1655654400 表示 key3 在时间戳 1655654400 后过期(精确到秒);pexpireat <key> <n>: 设置 key 在某个时间戳(精确到亳秒)之后过期,比如 pexpireat key4 1655654400000 表示 key4 在时间戳 1655654400000 后过期(精确到毫秒)
当然,在设置字符串时,也可以同时对 key 设置过期时间,共有3种命令:
set <key><value> ex <n>:设置键值对的时候,同时指定过期时间(精确到秒);set <key> <value> px <n>:设置键值对的时候,同时指定过期时间(精确到毫秒);setex <key><n> <valule>:设置键值对的时候,同时指定过期时间(精确到秒)
如果你想查看某个 key 剩余的存活时间,可以使用 TTL <key> 命令。
#设置键值对的时候,同时指定过期时间位 60 秒
> setex key1 60 value1
> OK
# 查看 key1 过期时间还剩多少
> ttl key1
(integer)56
> ttl key1
(integer)52
如果突然反悔,取消 key 的过期时间,则可以使用 PERSIST <key>命令
# 取消 key1 的过期时间
> persist key1
(integer)1
# 使用完 persist 命令之后,
#查下 key1 的存活时间结果是 -1,表明 key1 永不过期
>ttl key1
>(integer)-1
如何判定key已过期
每当我们对一个 key 设置了过期时间时,Redis 会把该 key 带上过期时间存储到一个过期字典(expiresdict)中,也就是说「过期字典」保存了数据库中所有 key 的过期时间。
过期字典存储在 redisDb 结构中,如下:
typedef struct redisDb {
dict *dict; /*数据库键空间,存放着所有的键值对 */
dict *expires; /* 键的过期时间 */
...
} redisDb;
过期字典数据结构结构如下
- 过期字典的 key 是一个指针,指向某个键对象;
- 过期字典的 value 是一个 long long 类型的整数,这个整数保存了 key 的过期时间;
过期字典的数据结构如图所示:

字典实际上是哈希表,哈希表的最大好处就是让我们可以用O(1)的时间复杂度来快速查找。当我们查询一个key时,Redis首先检查该key是否存在于过期字典中:
- 如果不在,则正常读取键值;
- 如果存在,则会获取该key的过期时间,然后与当前系统时间进行比对,如果比系统时间大,那就没有过期,否则判定该key已过期。
过期删除策略有哪些
在说Redis过期删除策略之前,先介绍下常见的三种过期策略:
- 定时删除;
- 惰性删除;
- 定期删除;
定时删除策略是怎么样的
定时删除策略的做法是,在设置 key 的过期时间时,同时创建一个定时事件,当时间到达时,由事件处理器自动执行 key 的删除操作。
定时删除策略的优点:
- 可以保证过期 key 会被尽快删除,也就是内存可以被尽快地释放。因此,定时删除对内存是最友好的。
定时删除策略的缺点:
- 在过期 key 比较多的情况下,删除过期 key 可能会占用相当一部分 CPU 时间,在内存不紧张但 CPU时间紧张的情况下,将 CPU 时间用于删除和当前任务无关的过期键上,无疑会对服务器的响应时间和吞吐量造成影响。所以,定时删除策略对 CPU 不友好。
惰性删除策略是怎么样的
惰性删除策略的做法是,不主动删除过期键,每次从数据库访问 key 时,都检测 key 是否过期,如果过期则删除该 key
惰性删除策略的优点:
- 因为每次访问时,才会检查 key 是否过期,所以此策略只会使用很少的系统资源,因此,惰性删除策略对 CPU 时间最友好。
惰性删除策略的缺点:
- 如果一个 key 已经过期,而这个 key 又仍然保留在数据库中,那么只要这个过期 key 一直没有被访问,它所占用的内存就不会释放,造成了一定的内存空间浪费。所以,惰性删除策略对内存不友好。
定期删除策略是怎么样的
定期删除策略的做法是,每隔一段时间「随机」从数据库中取出一定数量的 key 进行检查,并删除其中的过期key
定期删除策略的优点:
- 来减少删除操作对 CPU 的影响,同时也能删除一部分过期的数。通过限制删除操作执行的时长和频率,据减少了过期键对空间的无效占用。
定期删除策略的缺点:
- 内存清理方面没有定时删除效果好,同时没有惰性删除使用的系统资源少
- 难以确定删除操作执行的时长和频率。如果执行的太频繁,定期删除策略变得和定时删除策略一样,对CPU不友好;如果执行的太少,那又和惰性删除一样了,过期 key 占用的内存不会及时得到释放。
Redis过期删除策略是什么
Redis选择「惰性删除+定期删除」这两种策略配合使用,以求在合理使用CPU时间和避免内存浪费之间取得平衡。
Redis是怎么实现惰性删除的
Redis的惰性删除策略由db.c文件中的expireIfNeeded函数实现
int expireIfNeeded(redisDb *db, robj *key){
// 判断 key 是否过期
if(!keyIsExpired(db,key)) return 0;
...
/* 删除过期键 */
...
// 如果 server.lazyfree_lazy_expire 为 1 表示异步删除,反之同步删除;
return server.lazyfree lazy_expire ? dbAsyncDelete(db,key) :
dbsyncDelete(db,key);
Redis 在访问或者修改 key 之前,都会调用 expirelfNeeded 函数对其进行检查,检查 key 是否过期:
- 如果过期,则删除该key,至于选择异步删除,还是选择同步删除,根据 lazyfree_lazy_expire 参数配置决定(Redis 4.0版本开始提供参数),然后返回 null 客户端:
- 如果没有过期,不做任何处理,然后返回正常的键值对给客户端;
Redis是怎么实现定期删除的
再回忆一下,定期删除策略的做法:每隔一段时间「随机」从数据库中取出一定数量的 key 进行检查,并删除其中的过期key。
1、这个间隔检查的时间是多长呢?
在 Redis 中,默认每秒进行 10 次过期检查一次数据库,此配置可通过 Redis 的配置文件 redis.conf 进行配置,配置键为 hz 它的默认值是 hz 10。
特别强调下,每次检查数据库并不是遍历过期字典中的所有 key,而是从数据库中随机抽取一定数量的key 进行过期检查。
2、随机抽查的数量是多少呢?
我查了下源码,定期删除的实现在 expire.c 文件下的 activeExpirecycle 函数中,其中随机抽查的数量由 ACTIVE EXPIRE CYCLE LOOKUPS PER LOOP 定义的,它是写死在代码中的,数值是 20
也就是说,数据库每轮抽查时,会随机选择 20 个 key 判断是否过期。
接下来,详细说说 Redis 的定期删除的流程
- 1.从过期字典中随机抽取 20 个 key;
- 2.检查这 20 个 key 是否过期,并删除已过期的 key;
- 3.如果本轮检查的已过期 key 的数量,超过5个(20/4),也就是「已过期 key 的数量」占比「随机抽取key 的数量」大于 25%,则继续重复步骤 1;如果已过期的 key 比例小于 25%,则停止继续删除过期key,然后等待下一轮再检查。
可以看到,定期删除是一个循环的流程
那 Redis 为了保证定期删除不会出现循环过度,导致线程卡死现象,为此增加了定期删除循环流程的时间上限,默认不会超过 25ms。

浙公网安备 33010602011771号