查看某个 key 剩余的存活时间,可以使用 TTL 命令(单位是秒)。
取消 key 的过期时间,则可以使用 PERSIST 命令。
# 取消 key1 的过期时间
> persist key1
(integer) 1
# 使用完 persist 命令之后,
# 查下 key1 的存活时间结果是 -1,表明 key1 永不过期
> ttl key1
(integer) -1
过期字典就是一个哈希表,和保存键值对的字典(包含了两个哈希表,一个是真正保存键值对的,一个是在rehash时使用的)不同,它保存的是键和键对应的过期时间
typedef struct redisDb {
dict *dict; /* 数据库键空间,存放着所有的键值对 */
dict *expires; /* 键的过期时间 */
....
} redisDb;
通过查询这个过期字典,就可以知道某个key是否有过期时间。当我们查询一个key时,Redis会先去过期字典中查询,如果不存在,则直接在另一个字典(保存键值对的字段)中查询出对应的value返回;如果这个key存在于过期字典中,就查询对应的过期时间和当前系统时间进行对比,如果小于当前系统时间,就说明过期了
Redis的过期删除策略有三种,分别是“定时删除”、“惰性删除”、“定期删除”
定时删除是设置key的过期时间时,为当前的key设置一个定时事件,当这个key过期时,就会触发定时处理器处理这个定时事件,对key进行删除
设置key的过期时间可能到了,但是不会主动删除,而是在每次数据库访问key的时候,顺便看看这个key是不是过期了,发现过期了再删除
功能伪代码:
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);
}
每隔一段时间随机地从数据库中取出一部分key进行检查是否过期,过期了再删除。是一种介于定时删除和惰性删除之间的策略
功能伪代码:
循环中每次从过期字典中抽取20个key检查是否过期,如果过期则删除,同时记录本轮20个key中过期的key的数量expired,循环检查完20个key后判断expired是否操作检查的key的25%,超过则进入下一检查。为了避免不定地检查,会设置一个timelimit_exit限制检查是否检查时间超时
do {
//已过期的数量
expired = 0;
//随机抽取的数量
num = 20;
while (num--) {
//1. 从过期字典中随机抽取 1 个 key
//2. 判断该 key 是否过期,如果已过期则进行删除,同时对 expired++
}
// 超过时间限制则退出
if (timelimit_exit) return;
/* 如果本轮检查的已过期 key 的数量,超过 25%,则继续随机抽查,否则退出本轮检查 */
} while (expired > 20/4);
Redis采用的是定期删除和惰性删除两种策略配合使用,兼顾合理使用CPU资源和避免内存的浪费
过期删除策略是在大小键值对过期的时候对键值对进行删除的一种策略,而内存淘汰策略,是在内存超过了Redis设置的最大内存时使用内存淘汰策略删除符合条件的键值对
在配置文件 redis.conf 中,可以通过参数 maxmemory 来设定最大运行内存。不同位数的操作系统的默认值是不同的。对于64位的操作系统,默认值为0,也就是没有限制Redis的运行内存,就算实例因内存空间不足而奔溃也置之不理。对于32为的操作系统,默认值为3GB,因为32位操作系统的最大运行内存是4GB,设置3GB是合理的,可以避免Redis内存不足时奔溃。
Redis的内存淘汰策略分为“不进行数据淘汰的策略”(Redis3.0之后默认的内存淘汰策略)和“进行数据淘汰的策略”
Redis运行内存超过了设置的最大运行内存时,只是不允许数据写入,但是查询和删除操作还是可以进行的
进行数据淘汰的策略分为“在设置了过期时间的数据中淘汰”和“在所有数据范围内淘汰”
注意Redis3.0之前是从过期的数据中采用LRU算法进行淘汰
可以通过命令config set maxmemory-policy <策略>
设置,会立即生效,但是Redis重启后就失效了;也可以修改Redis配置文件中的maxmemory-policy <策略>
,然后重启Redis生效
LRU算法是最近最久未使用算法,Redis4.0后,内存淘汰策略中,对于“过期数据淘汰”和“所有数据进行淘汰”的策略中,都新增了LFU的内存淘汰策略,可见LRU算法的缺陷
Redis不是采用传统的LRU算法,而是在每个对象结构体中包含了一个额外字段,记录了这个对象的最后一次访问时间,这样,在进行内存淘汰的时候,直接随机抽取5(默认)个对象,然后淘汰最久没有使用的那个
LRU算法下Redis对象结构体中的lru字段存储的是对象最后一次访问的时间戳,而LFU算法下,Redis对象结构体的lru字段存储的则是对象的访问信息(包括最后一次访问的时间戳 + 访问频次)
last decr time是用来记录key最后一次访问的时间戳
logc的初始值是5,它代表的是key的访问频率,而不是具体的访问次数,会随着时间推移而衰减。数值越小越可能被淘汰。具体而言,每次key被访问的时候,它会根据上次访问距离当前的时长,来对logc进行衰减,距离越长,衰减值越大。然后,再按照一定的概率对logc进行增加,logc值越大的key,越难再增加。
可以通过调整配置文件中的lfu-decay-time
和lfu-log-factor
来调整logc的衰减速度和增长速度