共计 3233 个字符,预计需要花费 9 分钟才能阅读完成。
作者:千山 qianshan
juejin.im/post/5d6bda096fb9a06acc009dc8
相熟 Redis 的同学应该晓得,Redis 的每个 Key 都能够设置一个过期工夫,当达到过期工夫的时候,这个 key 就会被主动删除。
在为 key 设置过期工夫须要留神的事项
1、DEL/SET/GETSET 等命令会革除过期工夫
在应用 DEL、SET、GETSET 等会笼罩 key 对应 value 的命令操作一个设置了过期工夫的 key 的时候,会导致对应的 key 的过期工夫被革除。
// 设置 mykey 的过期工夫为 300s
127.0.0.1:6379> set mykey hello ex 300
OK
// 查看过期工夫
127.0.0.1:6379> ttl mykey
(integer) 294
// 应用 set 命令笼罩 mykey 的内容
127.0.0.1:6379> set mykey olleh
OK
// 过期工夫被革除
127.0.0.1:6379> ttl mykey
(integer) -1
2、INCR/LPUSH/HSET 等命令则不会革除过期工夫
而在应用 INCR/LPUSH/HSET 这种只是批改一个 key 的 value,而不是笼罩整个 value 的命令,则不会革除 key 的过期工夫。
INCR:
// 设置 incr_key 的过期工夫为 300s
127.0.0.1:6379> set incr_key 1 ex 300
OK
127.0.0.1:6379> ttl incr_key
(integer) 291
// 进行自增操作
127.0.0.1:6379> incr incr_key
(integer) 2
127.0.0.1:6379> get incr_key
"2"
// 查问过期工夫,发现过期工夫没有被革除
127.0.0.1:6379> ttl incr_key
(integer) 277
LPUSH:
// 新增一个 list 类型的 key,并增加一个为 1 的值
127.0.0.1:6379> LPUSH list 1
(integer) 1
// 为 list 设置 300s 的过期工夫
127.0.0.1:6379> expire list 300
(integer) 1
// 查看过期工夫
127.0.0.1:6379> ttl list
(integer) 292
// 往 list 外面增加值 2
127.0.0.1:6379> lpush list 2
(integer) 2
// 查看 list 的所有值
127.0.0.1:6379> lrange list 0 1
1) "2"
2) "1"
// 能看到往 list 外面增加值并没有使过期工夫革除
127.0.0.1:6379> ttl list
(integer) 252
3、PERSIST 命令会革除过期工夫
当应用 PERSIST 命令将一个设置了过期工夫的 key 转变成一个长久化的 key 的时候,也会革除过期工夫。
127.0.0.1:6379> set persist_key haha ex 300
OK
127.0.0.1:6379> ttl persist_key
(integer) 296
// 将 key 变为长久化的
127.0.0.1:6379> persist persist_key
(integer) 1
// 过期工夫被革除
127.0.0.1:6379> ttl persist_key
(integer) -1
4、应用 RENAME 命令,老 key 的过期工夫将会转到新 key 上
在应用例如:RENAME KEY_A KEY_B 命令将 KEY_A 重命名为 KEY_B,不论 KEY_B 有没有设置过期工夫,新的 key KEY_B 将会继承 KEY_A 的所有个性。
// 设置 key_a 的过期工夫为 300s
127.0.0.1:6379> set key_a value_a ex 300
OK
// 设置 key_b 的过期工夫为 600s
127.0.0.1:6379> set key_b value_b ex 600
OK
127.0.0.1:6379> ttl key_a
(integer) 279
127.0.0.1:6379> ttl key_b
(integer) 591
// 将 key_a 重命名为 key_b
127.0.0.1:6379> rename key_a key_b
OK
// 新的 key_b 继承了 key_a 的过期工夫
127.0.0.1:6379> ttl key_b
(integer) 248
这里篇幅无限,我就不一一将 key_a 重命名到 key_b 的各个状况列出来,大家能够在本人电脑上试一下 key_a 设置了过期工夫,key_b 没设置过期工夫这种状况。
5、应用 EXPIRE/PEXPIRE 设置的过期工夫为正数或者应用 EXPIREAT/PEXPIREAT 设置过期工夫戳为过来的工夫会导致 key 被删除
EXPIRE:
127.0.0.1:6379> set key_1 value_1
OK
127.0.0.1:6379> get key_1
"value_1"
// 设置过期工夫为 -1
127.0.0.1:6379> expire key_1 -1
(integer) 1
// 发现 key 被删除
127.0.0.1:6379> get key_1
(nil)
EXPIREAT:
127.0.0.1:6379> set key_2 value_2
OK
127.0.0.1:6379> get key_2
"value_2"
// 设置的工夫戳为过来的工夫
127.0.0.1:6379> expireat key_2 10000
(integer) 1
//key 被删除
127.0.0.1:6379> get key_2
(nil)
6、EXPIRE 命令能够更新过期工夫
对一个曾经设置了过期工夫的 key 应用 expire 命令,能够更新其过期工夫。
// 设置 key_1 的过期工夫为 100s
127.0.0.1:6379> set key_1 value_1 ex 100
OK
127.0.0.1:6379> ttl key_1
(integer) 95
更新 key_1 的过期工夫为 300s
127.0.0.1:6379> expire key_1 300
(integer) 1
127.0.0.1:6379> ttl key_1
(integer) 295
在 Redis2.1.3 以下的版本中,应用 expire 命令更新一个曾经设置了过期工夫的 key 的过期工夫会失败。并且对一个设置了过期工夫的 key 应用 LPUSH/HSET 等命令批改其 value 的时候,会导致 Redis 删除该 key。
Redis 的过期策略
那你有没有想过一个问题,Redis 外面如果有大量的 key,怎样才能高效的找出过期的 key 并将其删除呢,难道是遍历每一个 key 吗?如果同一期间过期的 key 十分多,Redis 会不会因为始终解决过期事件,而导致读写指令的卡顿。
这里阐明一下,Redis 是单线程的,所以一些耗时的操作会导致 Redis 卡顿,比方当 Redis 数据量特地大的时候,应用 keys * 命令列出所有的 key。
实际上 Redis 应用懈怠删除 + 定期删除相结合的形式解决过期的 key。
懈怠删除
所谓懈怠删除就是在客户端拜访该 key 的时候,redis 会对 key 的过期工夫进行查看,如果过期了就立刻删除。
这种形式看似很完满,在拜访的时候查看 key 的过期工夫,不会占用太多的额定 CPU 资源。然而如果一个 key 曾经过期了,如果长时间没有被拜访,那么这个 key 就会始终存留在内存之中,重大耗费了内存资源。
定期删除
定期删除的原理是,Redis 会将所有设置了过期工夫的 key 放入一个字典中,而后每隔一段时间从字典中随机一些 key 查看过期工夫并删除已过期的 key。
Redis 默认每秒进行 10 次过期扫描:
- 从过期字典中随机 20 个 key
- 删除这 20 个 key 中已过期的
- 如果超过 25% 的 key 过期,则反复第一步
同时,为了保障不呈现循环适度的状况,Redis 还设置了扫描的工夫下限,默认不会超过 25ms。