Redis——过期时间/过期回收策略
既然是缓存,就会涉及过期时间以及过期后清理回收内存的过程;本篇主要讲一下redis的key过期时间相关的命令、注意事项、回收策略;
注意:实际上,redis的内存回收触发有两种情况,上面说的是一种,也就是我们设置了过期的对象到期的时候触发的到期释放的内存回收,还有一种是内存使用达到maxmemory上限时候触发的溢出回收。本篇主要讲前者,后者可以参考《Redis——内存消耗及回收》。
1. 概念
生存时间:(Time To Live, TTL),经过指定的秒/毫秒之后,服务器自动删除TTL为0的key
过期时间:(expire time),时间戳,表示一个具体时间点,到这个时间点后,服务器会删除key
2. 相关命令
(1)设置生存时间TTL
EXPIRE key ttl #设置ttl,单位s
PEXPIRE key ttl #设置ttl,单位ms
可以对一个已经带有生存时间的 key 执行 EXPIRE 命令,新指定的生存时间会取代旧的生存时间。
EXPIRE key seconds
为给定 key 设置生存时间,当 key 过期时(生存时间为 0 ),它会被自动删除。
在 Redis 中,带有生存时间的 key 被称为『易失的』(volatile)。
生存时间可以通过使用 DEL 命令来删除整个 key 来移除,或者被 SET 和 GETSET 命令覆写(overwrite),这意味着,如果一个命令只是修改(alter)一个带生存时间的 key 的值而不是用一个新的 key 值来代替(replace)它的话,那么生存时间不会被改变。
比如说,对一个 key 执行 INCR 命令,对一个列表进行 LPUSH 命令,或者对一个哈希表执行 HSET 命令,这类操作都不会修改 key 本身的生存时间。
另一方面,如果使用 RENAME 对一个 key 进行改名,那么改名后的 key 的生存时间和改名前一样。
RENAME 命令的另一种可能是,尝试将一个带生存时间的 key 改名成另一个带生存时间的 another_key ,这时旧的 another_key (以及它的生存时间)会被删除,然后旧的 key 会改名为 another_key ,因此,新的 another_key 的生存时间也和原本的 key 一样。
使用 PERSIST 命令可以在不删除 key 的情况下,移除 key 的生存时间,让 key 重新成为一个『持久的』(persistent) key 。
PEXPIRE key milliseconds
这个命令和 EXPIRE 命令的作用类似,但是它以毫秒为单位设置 key 的生存时间,而不像 EXPIRE 命令那样,以秒为单位。
返回值:
设置成功返回 1 。
当 key 不存在或者不能为 key 设置生存时间时(比如在低于 2.1.3 版本的 Redis 中你尝试更新 key 的生存时间),返回 0 。
示例:
redis> SET cache_page "www.AA.com"
OKredis> EXPIRE cache_page 30 # 设置过期时间为 30 秒
(integer) 1redis> TTL cache_page # 查看剩余生存时间
(integer) 23redis> EXPIRE cache_page 30000 # 更新过期时间
(integer) 1redis> TTL cache_page
(integer) 29996
(2)设置过期时间 (指定过期的时间节点)
EXPIREAT key timestamp #设置expire time,s
PEXPIREAT key timestamp #设置exprie time,ms
EXPIREAT key timestamp
EXPIREAT 的作用和 EXPIRE 类似,都用于为 key 设置生存时间。
不同在于 EXPIREAT 命令接受的时间参数是 UNIX 时间戳(unix timestamp)。
PEXPIREAT key milliseconds-timestamp
这个命令和 EXPIREAT 命令类似,但它以毫秒为单位设置 key 的过期 unix 时间戳。
过期时间的精确度
在 Redis 2.4 版本中,过期时间的延迟在 1 秒钟之内 —— 也即是,就算 key 已经过期,但它还是可能在过期之后一秒钟之内被访问到,而在新的 Redis 2.6 版本中,延迟被降低到 1 毫秒之内。
以上4种命令虽然各有不同,但是其底层都是使用 PEXPIREAT 实现的!
(3)删除和更新
PERSIST key #移除生存时间
PERSIST key
移除给定 key 的生存时间,将这个 key 从『易失的』(带生存时间 key )转换成『持久的』(一个不带生存时间、永不过期的 key )。
DLE 命令可以删除key,也会删除其生存时间
SET 和 GETSET 命令也可以覆写生存时间
(4)查看剩余存活时间
TTL key #计算key的剩余生存时间,s
PTTL key #计算key的剩余生存时间,ms
TTL key
以秒为单位,返回给定 key 的剩余生存时间(TTL, time to live)。
PTTL key
这个命令类似于 TTL 命令,但它以毫秒为单位返回 key 的剩余生存时间。
返回值:
当 key 不存在时,返回 -2 。
当 key 存在但没有设置剩余生存时间时,返回 -1 。
否则,以秒为单位,返回 key 的剩余生存时间。
3. 原理:过期时间如何保存
redisDb结果的expires字典中保存了数据库中的所有key的过期时间,redisDb的声明如下:
/* Redis database representation. There are multiple databases identified* by integers from 0 (the default database) up to the max configured* database. The database number is the 'id' field in the structure. */
//每个数据库都是一个redisDb,id为数据库编号
typedef struct redisDb {dict *dict; //键空间,保存了数据中所有键值对dict *expires; //过期字典,保存了数据库中所有键的过期时间dict *blocking_keys;dict *ready_keys;dict *watched_keys;struct evictionPoolEntry *eviction_pool;int id; /* Database ID */long long avg_ttl; /* Average TTL, just for stats */
} redisDb;
expires 的键是一个指针,指向某个键对象,值是一个 long long 类型整数,保存了过期时间,是一个毫秒精度的UNIX时间戳
可见,过期时间的保存是使用key来作为关联的,所以操作用,修改key均可以修改过期时间,而只修改key的value,是不是改变其过期时间的;
如何计算过期时间?
底层的处理方式也很简单,获取key的生存时间戳,减去当前时间戳即可;
如果键不存在,则返回-2;
如果键没有设置过期时间,则返回-1;
同样可以使用此方法判断key是否过期,TTL/PTTL 结果小于0,则表示过去,大于0,则表示未过期;
4. Redis的key过期删除策略
有哪些过期删除策略?
定时删除:设置键的过期时间的同时,设置一个定时器,来删除键
惰性删除:放任过期键不管,每次从键空间取值时,检查是否过期,以决定是否删除;
定期删除:每隔一段时间,进行一次数据库检查,删除里面的过期键,至于,要删除多少过期键,以及要检查多少数据库,由算法决定;
各自的利弊?
(1)定时删除
定时删除是指在设置键的过期时间的同时,创建一个定时器,让定时器在键的过期时间来临时,立即执行对键的删除操作。
定时删除策略对内存是最友好的:通过使用定时器,定时删除策略可以保证过期键会尽可能快的被删除,并释放过期键所占用的内存。
定时删除策略的缺点是,他对CPU时间是最不友好的:再过期键比较多的情况下,删除过期键这一行为可能会占用相当一部分CPU时间。
除此之外,创建一个定时器需要用到Redis服务器中的时间事件。而当前时间事件的实现方式----无序链表,查找一个事件的时间复杂度为O(N)----并不能高效地处理大量时间事件。
(2)惰性删除
惰性删除是指放任键过期不管,但是每次从键空间中获取键时,都检查取得的键是否过期,如果过期的话就删除该键,如果没有过期就返回该键。
惰性删除策略对CPU时间来说是最友好的,但对内存是最不友好的。如果数据库中有非常多的过期键,而这些过期键又恰好没有被访问到的话,那么他们也许永远也不会被删除。
Redis 的惰性删除策略由 db.c/expireIfNeeded 函数实现,所有读写Redis的命令在执行之前都会调用expireIfNeeded 函数
(3)定期删除
定期删除是指每隔一段时间,程序就对数据库进行一次检查,删除里面的过期键。
定期删除策略是前两种策略的一种整合和折中:
- 定期删除策略每隔一段时间执行一次删除过期键操作,并通过限制删除操作执行的时长和频率来减少删除操作对CPU时间的影响。
- 除此之外,通过定期删除过期键,定期删除策略有效地减少了因为过期键带来的内存浪费。
定期删除策略的难点是确定删除操作执行的时长和频率:
- 如果删除操作执行的太频繁或者执行的时间太长,定期删除策略就会退化成定时删除策略,以至于将CPU时间过多的消耗在删除过期键上面。
- 如果删除操作执行的太少,或者执行的时间太短,定期删除策略又会和惰性删除策略一样,出现浪费内存的情况。
redis的过期删除策略?
那你有没有想过一个问题,Redis里面如果有大量的key,怎样才能高效的找出过期的key并将其删除呢,难道是遍历每一个key吗?假如同一时期过期的key非常多,Redis会不会因为一直处理过期事件,而导致读写指令的卡顿。
这里说明一下,Redis是单线程的,所以一些耗时的操作会导致Redis卡顿,比如当Redis数据量特别大的时候,使用keys * 命令列出所有的key。
Redis所有的键都可以设置过期属性,内部保存在过期字典中。由于进程内保存大量的键,维护每个键精准的过期删除机制会导致消耗大量的 CPU,对于单线程的Redis来说成本过高。
因此——Redis服务器实际使用的是惰性删除和定期删除两种策略:通过配合使用这两种删除策略,服务器可以很好的在合理使用CPU时间和避免浪费内存空间之间取得平衡。
惰性删除:顾名思义,指的是不主动删除,当用户访问已经过期的对象的时候才删除种方式看似很完美,在访问的时候检查key的过期时间,最大的优点是节省cpu的开销,不用另外的内存和TTL链表来维护删除信息。但是如果一个key已经过期了,如果长时间没有被访问,那么这个key就会一直存留在内存之中,严重消耗了内存资源。
定时任务删除:为了弥补第一种方式的缺点,redis内部还维护了一个定时任务,默认每秒运行10次。定时任务中删除过期逻辑采用了自适应算法,使用快、慢两种速率模式回收键。
定期删除:Redis会将所有设置了过期时间的key放入一个字典中,然后每隔一段时间从字典中随机一些key检查过期时间并删除已过期的key。
Redis默认每秒进行10次过期扫描:
- 从过期字典中随机20个key
- 删除这20个key中已过期的
- 如果超过25%的key过期,则重复第一步
同时,为了保证不出现循环过度的情况,Redis还设置了扫描的时间上限,默认不会超过25ms。
图示:
流程说明:
1. 定时任务在每个数据库空间随机检查20个键,当发现过期时删除对应的键。
2. 如果超过检查数25%的键过期,循环执行回收逻辑直到不足25%或 运行超时为止,慢模式下超时时间为25毫秒。
3. 如果之前回收键逻辑超时,则在Redis触发内部事件之前再次以快模 式运行回收过期键任务,快模式下超时时间为1毫秒且2秒内只能运行1次。
4. 快慢两种模式内部删除逻辑相同,只是执行的超时时间不同。
定期删除策略的实现
过期键的定期删除策略由函数redis.c/activeExpireCycle实现,每当Redis服务器周期性操作redis.c/serverCron函数执行时,activeExpireCycle函数就会被调用,它在规定的时间内分多次遍历服务器中的各个数据库,从数据库的expires字典中随机检查一部分键的过期时间,并删除其中的过期键。
5. redis过期时间的注意事项
(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,需要注意哪些问题? - SegmentFault 思否
Redis中的key的生存时间和过期时间 - sherlock_lin - 博客园
Redis学习——内存消耗和内存回收机制
redis内存回收机制
Redis——过期时间/过期回收策略相关推荐
- Redis-设置过期时间及淘汰策略
文章目录 1. TTL 2. 设置过期时间 3. 删除过期key 4. 淘汰策略 Redis-设置过期时间及淘汰策略 项目组使用的 Redis 服务器发出了内存不足报警,查了一些资料,记录下. 1. ...
- c# redis hashid如何设置过期时间_Redis数据库实现原理(划重点)
Redis服务器将所有数据库都保存在服务器状态redis.h/redisServer结构的db数组中,db数组的每一项都是一个redis.h/redisDb结构,每个redisDb结构代表一个数据库, ...
- redis续期_redis分布式锁自动延长过期时间
背景 项目组已经有个 分布式锁注解(参考前文<记一次分布式锁注解化>),但是在设置锁过期时间时,需要去预估业务耗时时间,如果锁的过期时间能根据业务运行时间自动调整,那使用的就更方便了. 思 ...
- java redis set 过期时间_redis分布式锁自动延长过期时间
分布式系统概念与设计(原书第5版) 93.8元 包邮 (需用券) 去购买 > 背景项目组已经有个分布式锁注解(参考前文<记一次分布式锁注解化>),但是在设置锁过期时间时,需要去预估业 ...
- 【高并发】面试官:说说缓存最关心的问题?有哪些类型?回收策略和算法?...
写在前面 往往开始做一个项目时,不会过多的考虑性能问题,以快速迭代功能为主.后续随着业务的快速发展,系统运行的性能越来越慢,此时,就需要对系统进行相应的优化,而效果最显著的就是给系统加上缓存.那么,问 ...
- Redis 缓存回收的7种策略volatile设置过期时间及allkeys所有数据范围内
1.基础说明 当redis设置内存使用限制后,当达到内存限制时,Redis将尝试删除key(控制节点的最大使用内存) redis.conf中配置项maxmemory <bytes>或者控制 ...
- java redis 数据自过期_Java架构-Redis的内存回收策略和Key过期策略,看这篇就够了...
Redis 作为当下最热门的 Key-Value 存储系统,在大大小小的系统中都扮演着重要的角色,不管是 session 存储还是热点数据的缓存,亦或是其他场景,我们都会使用到 Redis.在生产环境 ...
- Redis过期时间及过期策略
一.基本命令 Redis中我们可以通过EXPIRE和PEXPIRE来设置键的生存时间(TTL),通过命令,客户端可以经过指定的秒或者毫秒为精度,对数据库中的特定键设置生存时间: 同样,我们可以通过EX ...
- c# redis hashid如何设置过期时间_Redis中Key过期策略amp;淘汰机制
1. Redis中设置Key过期时间 我们有两种方式设置过期时间 1.1 设置多久后过期 设置一个 key 10s 过期,可以这样 127.0.0.1:6379> SET key value E ...
最新文章
- 1:1 人脸比对 开源_Hacktoberfest:我的开源门户
- 文科生也能当工程师?我用了一年半
- R语言ggplot2可视化:应用pivot_longer函数将数据从宽格式转换为长格式、为dataframe的每一列绘制密度图和直方图(堆叠)
- [Linux网络编程学习笔记]套接字地址结构
- Entity Framework在WCF中序列化的问题
- CSDN总结的面试中的十大可视化工具
- 解决动态规划问题4步曲
- 用c语言递归函数实现焚天塔的过程,梵天塔问题.PPT
- NutzWk 5.0.x 微服务分布式版本开发及部署说明
- MacBook Pro 2017 13寸版 触摸板windows驱动开发(开发HID鼠标键盘驱动之一)
- videojs播放器插件使用详解
- 虚拟服务器的密码忘记了,虚拟平台管理术:忘记 ESXi 主机的 root 密码该怎么办?...
- Android 双卡双待识别
- 微信小程序例子——使用icon组件显示常用图标
- arnold渲染器预览窗口打开时保存有可能崩溃,解决方法如下
- 详解设置路由导航的两种方法
- python3.7 安装 scrapy, pip 升级
- springboot影院售票小程序毕业设计-附源码201532
- python已知两边求第三边_已知两边求第三边公式
- 中考可以用计算机,中考计算机考试内容·中考信息技术要考哪些项目?
热门文章
- tf.name_scope与tf.variable_scope用法区别
- 科技云报道:“奇袭”混合云,青云QingCloud站上C位
- 前端ajax实现分页思路详解
- js判断Lodop驱动是否安装
- CSS盒子边框(border)样式综合样式
- 2.4 旋转曲面 (1)
- 非飞行模式下,笔记本电脑不显示附近WiFi,并提示“适配器遇到与驱动程序或硬件相关的问题”——解决办法
- 点击按钮随机更换页面背景颜色
- uni-app 上传图片到阿里云oss
- 必过SafetyNet!以MIUI开发版系统为例详解Android设备通过SafetyNet校验方法