Redis最大的特点是不仅可以保存key/value数据对象,还可以对这个键设置过期时间,有没有想过在设置过期时间时,Redis是如何维护这些key,和普通的没有设置过期时间的key有什么区别吗?当Key的过期时间到期之后,Redis是通过什么方式来删除这些Key的呢?带着这些问题我们一起看看吧~
Redis的数据库
在了解键的过期机制之前,我们先了解一下Redis中的数据库,当使用Redis可视化工具时,连接到Redis时,可以看到很多个数据库,在执行select
命令时就会切换数据库。
可以看到有16个数据库,每个数据库都是相互隔离,互不影响,这主要是Redis关于数据库的设计导致。
-
Redis数据库结构
struct redisServer {
//...
// 一个数组,保存的是服务器中的所有数据组,数组中每个对象都是RedisDb
redisDb *db;
// 服务器的数据库数量,默认16个
int dbnum;
//...
}
// 每个数据库对象
typedef struct redisDb {
//...
// 数据库键空间,保存着数据库中的所有键值对
dict *dict;
//...
}其中RedisDb对象中保存的dict是一个字典,这个字典被称为键空间。
键空间中的键就是数据库中的键,是一个字符串对象。
键空间中的值就是数据库中的值,每个值可以是字符串对象、哈希对象、列表对象、集合对象、有序集合对象。
在执行Redis命令,例如:set、lpush、sadd等命令时,实质是在数据库的键空间中创建一个key,并将响应的值对象保存在键空间中,所以说Redis的key/value在Redis内部的保存的结构中就是一个字典。
过期键机制
如何设置键的过期时间
-
expire、pexpire
expire key 5 // 秒
pexpire key 5000 // 毫秒这两个命令都是为key设置过期时间,使用的方式基本相同,不过对于过期时间的单位不同,
expire
命令的过期时间是秒,pexpire
命令的过期时间是毫秒。 -
expireat、pexpireat
expireat key 1620225122 // 秒的精度
pexpireat key 1620225121000 // 毫秒精度与上面的命令相似,都是设置key的过期时间,不过唯一的区别是上面的命令是设置键的过期时间间隔,而这两个命令设置的是过期的时间戳,当到达时间戳时,表示键就会被删除。
虽然有不同的形式和时间单位来设置过期时间,实际上expire
、pexpire
、expireat
命令都是使用pexpireat
命令实现的,无论使用哪种方式设置过期时间,最终redis都会将设置的过期时间转换为时间戳并通过pexpireat
设置过期时间
过期时间的键是如何保存
上面介绍了redisDb的数据结构,可以了解到保存在数据库中的键值都是通过字典进行维护保存,同样的,在redisDb中,还有一个字典—过期字典,专门用来保存设置了过期时间的键,过期字典中保存了当前数据库中所有键的过期时间
-
redisDb的结构
// 每个数据库对象
typedef struct redisDb {
//...
// 过期键字典,保存过期键的过期时间
dict *expires;
//...
}过期字典同样保存在redisDb中,字典的键是过期键,字典的值是键对应的过期时间。
虽然保存了两份字典,不过不会出现浪费内存的情况,因为过期字典中的键是一个指针,这个指针指向键空间中的某个键对象(数据库字典中的键),过期字典的值是一个整数,这个整数保存了键空间中键对应的过期时间,是一个时间戳。
过期键删除策略
我们已经知道了数据库键的过期时间都保存在过期字典中,当一个键过期时间已经到了的时候,Redis是如何执行删除掉过期的键呢?这里需要了解一下过期键的删除策略
Redis提供了三种不同的删除策略:
-
定时删除(立即删除)
为每一个键设置一个定时期,当键的过期时间到了,那么就立即执行定时器,对键进行删除操作
-
优点:立即删除键,释放内存 -
缺点:会占用大量的CPU,当同一时间过期的键较多就会导致大量的删除操作,如果这时有较多的客户端请求,就会导致请求处理速度较慢 -
惰性删除
当键的过期时间到达的时候,并不立即进行删除,而是当访问到这个键的时候,检查这个键是否过期,如果已过期就删除并返回null
-
优点:不会占用过多的CPU,保证Redis的执行效率 -
缺点:会有大量的过期键还保存在内存中,占用额外的内存,并且如果一个过期键不会在访问了,那么这个过期键就会一直保存在内存中,可以看做是内存泄漏 -
定期删除
定期删除是前两种策略的一种折中方案:
定期删除会每隔一段时间(默认是1s扫描20次),从数据库中取出一定数量的随机键进行检查(默认是20个),删除其中的过期键,如果其中已经过期的键超过25%,那么就立即再次执行一次扫描删除操作。
当然不会无限制执行下去,定期删除会通过限制删除操作执行的时长和频率来防止无限制的执行定期删除操作导致CPU占用的影响
-
优点:不会占用过多的CPU和防止内存浪费等问题 -
缺点:如果定期删除执行的时长和频率不合理,就会退化为定时删除,占用过多的CPU时间
Redis使用的删除策略
综合了上面的3种删除策略,Redis使用的是惰性删除和定期删除两种策略,通过配合使用两种策略,服务器可以很好的在CPU和内存的使用之间取得平衡。
AOF、RDB、主从复制对过期键的处理
RDB
-
生成RDB文件
在通过
SAVE
或BGSAVE
命令创建一个新的RDB快照文件时,程序会对数据库中的键进行检查,已过期的键不会保存到RDB文件中 -
载入RDB文件
在启动Redis时,会通过RDB文件来恢复内存中的数据,在加载RDB文件时,Redis也会对文件中保存的键进行检查,未过期的键会被载入到数据库中,而过期键则会被忽略。
AOF
-
写入AOF
在数据库中的键已经过期时,还没有删除的时候,AOF文件不会发生变更,只有当这个过期键被删除策略进行删除之后,会向AOF文件中追加一条DEL命令,表示该键已经被删除
-
载入AOF
因为过期键在删除的时候,已经向AOF中追加一条DEL指令,所以尽管过期键可以被正常载入到内存,但是随后就会执行DEL命令删除该过期键。
-
AOF重写
在AOF重写的过程中,程序会对数据库中的键进行检查,只会向AOF文件中写入未过期的键,已过期的键不会被写入到AOF文件中。(和RDB文件类似)
主从复制
为了保证主从服务器的数据一致性,所以从服务器中没有过期键之分,从服务器不会触发删除策略
-
主服务器在保存一个过期键的时候,通过网络链接将命令同步发送给从服务器,但是当这个键在从服务器中过期,从服务器也不会删除该键,只有当主服务器删除一个过期键之后,会向从服务器发送一条DEL指令,告诉从服务器删除这个过期键。
小结
-
Redis的数据库都是一个数组,保存在 RedisServer
结构中 -
数据库主要由 dict
和expires
两个字典构成,其中dict
主要负责保存键值对,expires
主要负责保存过期键,并且不会导致键的内存浪费,两个字典公用一个键对象 -
Redis的删除过期键的策略,Redis使用惰性删除和定期删除两种策略 -
AOF在过期键删除时,如果服务器删除了会追加一条DEL命令,AOF重写时不会保存过期键数据到文件中 -
RDB在生成和载入时,已过期的键不会保存到RDB文件中,载入时也会检查键是否过期决定是否加载到内存中 -
主从复制中,从服务器不会主动删除过期键,只有到主服务器执行删除过期键之后,会向从服务器发送DEL命令,从服务器删除过期键。
原文始发于微信公众号(指尖上的代码):最详细的一篇关于Redis的过期键机制介绍
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/39942.html