浅谈 Redis 键过期策略
在 Redis 中,我们都知道 Redis 的键值对都是存储在内存中的,要是我们一直往 Redis 中添加键值对,最后就会导致 OOM,所以我们需要定期的去删除一些键值对,来保证内存不会爆,此时我们就需要了解 Redis 的键过期策略了
在学会键过期策略之前,我们还是先来讲解如何使用键过期策略吧😄😄😄
一、过期设置
Redis 中设置过期时间主要通过以下四种方式:
- expire key seconds:设置 key 在 n 秒后过期;
- pexpire key milliseconds:设置 key 在 n 毫秒后过期;
- expireat key timestamp:设置 key 在某个时间戳(精确到秒)之后过期;
- pexpireat key millisecondsTimestamp:设置 key 在某个时间戳(精确到毫秒)之后过期;
下面我们依次来演示这几种过期设置吧
expire:N 秒后过期
127.0.0.1:6379> set key value
OK
127.0.0.1:6379> expire key 100
(integer) 1
127.0.0.1:6379> ttl key
(integer) 97
其中命令 ttl 的全称是 Time To Live,表示此键值在 n 秒后过期。例如,上面的结果 97 表示 key 在 97s 后过期。
pexpire:N 毫秒后过期
127.0.0.1:6379> set key2 value2
OK
127.0.0.1:6379> pexpire key2 100000
(integer) 1
127.0.0.1:6379> pttl key2
(integer) 94524
其中 pexpire key2 100000
表示设置 key2 在 100000 毫秒(100 秒)后过期。
expireat:过期时间戳精确到秒
127.0.0.1:6379> set key3 value3
OK
127.0.0.1:6379> expireat key3 1573472683
(integer) 1
127.0.0.1:6379> ttl key3
(integer) 67
其中 expireat key3 1573472683
表示 key3 在时间戳 1573472683 后过期(精确到秒),使用 ttl 查询可以发现在 67s 后 key3 会过期。
小贴士:在 Redis 可以使用 time 命令查询当前时间的时间戳(精确到秒),示例如下:
127.0.0.1:6379> time 1. "1573472563" 2. "248426"
pexpireat:过期时间戳精确到毫秒
127.0.0.1:6379> set key4 value4
OK
127.0.0.1:6379> pexpireat key4 1573472683000
(integer) 1
127.0.0.1:6379> pttl key4
(integer) 3522
其中 pexpireat key4 1573472683000
表示 key4 在时间戳 1573472683000 后过期(精确到毫秒),使用 ttl 查询可以发现在 3522ms 后 key4 会过期。
字符串中的过期操作
字符串中几个直接操作过期时间的方法,如下列表:
- set key value ex seconds:设置键值对的同时指定过期时间(精确到秒);
- set key value px milliseconds:设置键值对的同时指定过期时间(精确到毫秒);
- setex key seconds valule:设置键值对的同时指定过期时间(精确到秒)。
实现示例如下:
1. set key value ex seconds
127.0.0.1:6379> set k v ex 100
OK
127.0.0.1:6379> ttl k
(integer) 97
2. set key value ex milliseconds
127.0.0.1:6379> set k2 v2 px 100000
OK
127.0.0.1:6379> pttl k2
(integer) 92483
3. setex key seconds valule
127.0.0.1:6379> setex k3 100 v3
OK
127.0.0.1:6379> ttl k3
(integer) 91
二、移除过期时间
当我们设置键的过期时间之后,其实我们还可以移除键的过期时间,可以使用命令 persist key
可以移除键值的过期时间,如下代码所示:
127.0.0.1:6379> ttl k3
(integer) 97
127.0.0.1:6379> persist k3
(integer) 1
127.0.0.1:6379> ttl k3
(integer) -1
可以看出第一次使用 ttl 查询 k3 会在 97s 后过期,当使用了 persist 命令之后,在查询 k3 的存活时间发现结果是 -1,它表示 k3 永不过期。
其他过期操作方法:
- pexpire(String key, long milliseconds):设置 n 毫秒后过期;
- expireAt(String key, long unixTime):设置某个时间戳后过期(精确到秒);
- pexpireAt(String key, long millisecondsTimestamp):设置某个时间戳后过期(精确到毫秒);
三、持久化中的过期键
RDB 中的过期键
DB 文件分为两个阶段,RDB 文件生成阶段和加载阶段。
1. RDB 文件生成
从内存状态持久化成 RDB(文件)的时候,会对 key 进行过期检查,过期的键不会被保存到新的 RDB 文件中,因此 Redis 中的过期键不会对生成新 RDB 文件产生任何影响。
2. RDB 文件加载
RDB 加载分为以下两种情况:
- 如果 Redis 是主服务器运行模式的话,在载入 RDB 文件时,程序会对文件中保存的键进行检查,过期键不会被载入到数据库中。所以过期键不会对载入 RDB 文件的主服务器造成影响;
- 如果 Redis 是从服务器运行模式的话,在载入 RDB 文件时,不论键是否过期都会被载入到数据库中。但由于主从服务器在进行数据同步时,从服务器的数据会被清空。所以一般来说,过期键对载入 RDB 文件的从服务器也不会造成影响。
AOF 中的过期键
1. AOF 文件写入
当 Redis 以 AOF 模式持久化时,如果数据库某个过期键还没被删除,那么 AOF 文件会保留此过期键,当此过期键被删除后,Redis 会向 AOF 文件追加一条 DEL 命令来显式地删除该键值。
2. AOF 重写
执行 AOF 重写时,会对 Redis 中的键值对进行检查已过期的键不会被保存到重写后的 AOF 文件中,因此不会对 AOF 重写造成任何影响。
四、主从库的过期键
当 Redis 运行在主从模式下时,从库不会进行过期扫描,从库对过期的处理是被动的。也就是即使从库中的 key 过期了,如果有客户端访问从库时,依然可以得到 key 对应的值,像未过期的键值对一样返回。
从库的过期键处理依靠主服务器控制,主库在 key 到期时,会在 AOF 文件里增加一条 del 指令,同步到所有的从库,从库通过执行这条 del 指令来删除过期的 key。
五、过期策略原理分析
讲完了过期键设置的基本使用,我们更加需要知道它的原理😂😂😂
过期键执行流程
Redis 之所以能知道那些键值过期,是因为在 Redis 中维护了一个字典,存储了所有设置了过期时间的键值,我们称之为过期字典。
过期键判断流程如下图所示:
过期键数据结构(字典)如下图所示:
六、过期策略
Redis 会删除已过期的键值,以此来减少 Redis 的空间占用,但因为 Redis 本身是单线的,如果因为删除操作而影响主业务的执行就得不偿失了,为此 Redis 需要制定多个(过期)删除策略来保证糟糕的事情不会发生。
常见的过期策略有以下三种:
- 定时删除
- 惰性删除
- 定期删除
下面分别来看每种策略有何不同吧
定时删除
在设置键值过期时间时,创建一个定时事件,当过期时间到达时,由事件处理器自动执行键的删除操作。
- 优点:保证内存可以被尽快地释放。
- 缺点:在 Redis 高负载的情况下或有大量过期键需要同时处理时,会造成 Redis 服务器卡顿,影响主业务执行。
惰性删除
不主动删除过期键,每次从数据库获取键值时判断是否过期,如果过期则删除键值,并返回 null
。
- 优点:因为每次访问时,才会判断过期键,所以此策略只会使用很少的系统资源。
- 缺点:系统占用空间删除不及时,导致空间利用率降低,造成了一定的空间浪费。
定期删除
每隔一段时间检查一次数据库,随机删除一些过期键。
Redis 默认每秒进行 10 次过期扫描,此配置可通过 Redis 的配置文件 redis.conf 进行配置,配置键为 hz 它的默认值是 hz 10
。
小贴士:Redis 每次扫描并不是遍历过期字典中的所有键,而是采用随机抽取判断并删除过期键的形式执行的。
定期删除流程
- 从过期字典中随机取出 20 个键;
- 删除这 20 个键中过期的键;
- 如果过期 key 的比例超过 25%,重复步骤 1。
同时为了保证过期扫描不会出现循环过度,导致线程卡死现象,算法还增加了扫描时间的上限,默认不会超过 25ms。
定期删除执行流程,如下图所示:
- 优点:通过限制删除操作的时长和频率,来减少删除操作对 Redis 主业务的影响,同时也能删除一部分过期的数据减少了过期键对空间的无效占用。
- 缺点:内存清理方面没有定时删除效果好,同时没有惰性删除使用的系统资源少。
小贴士:Redis 使用的是惰性删除加定期删除的过期策略。
七、小结
本文我们知道了 Redis 中的四种设置过期时间的方式:expire
、pexpire
、expireat
、pexpireat
,其中比较常用的是 expire
设置键值 n 秒后过期。
字符串中可以在添加键值的同时设置过期时间,并可以使用 persist
命令移除过期时间。同时我们也知道了过期键在 RDB 写入和 AOF 重写时都不会被记录。
过期键在主从模式下,从库对过期键的处理要完全依靠主库,主库删除过期键之后会发送 del
命令给所有的从库。
Redis 是通过设置过期字典的形式来判断过期键的,Redis 采用的是惰性删除和定期删除的形式删除过期键的,Redis 的定期删除策略并不会遍历删除每个过期键,而是采用随机抽取的方式删除过期键,同时为了保证过期扫描不影响 Redis 主业务,Redis 的定期删除策略中还提供了最大执行时间,以保证 Redis 正常并高效
巨人的肩膀:
http://learn.lianglianglee.com/专栏/Redis 核心原理与实战/18 Redis 过期策略与源码分析.md