Redis的内存回收机制主要体现在以下两个方面:
- 删除到达过期时间的键对象。
- 内存使用达到
maxmemory
上限时触发内存溢出控制策略。
过期删除策略
删除策略的目标:在内存占用与CPU占用之间寻找一种平衡,顾此失彼都会造成整体redis性能的下降,甚至引发服务器宕机或
内存泄露。
设置Redis键过期时间
先回顾一下Redis 提供的设置过期时间的命令:
- EXPIRE :表示将键 key 的生存时间设置为 ttl 秒。
- PEXPIRE :表示将键 key 的生存时间设置为 ttl 毫秒。
- EXPIREAT :表示将键 key 的生存时间设置为 timestamp 所指定的秒数时间戳。
- PEXPIREAT :表示将键 key 的生存时间设置为 timestamp 所指定的毫秒数时间戳。
在Redis内部实现中,前面三个设置过期时间的命令最后都会转换成最后一个PEXPIREAT 命令来完成。
其他相关命令还有:
- 移除键的过期时间 PERSIST :表示将key的过期时间移除。
- 返回键的剩余生存时间
- TTL :以秒的单位返回键 key 的剩余生存时间。
- PTTL :以毫秒的单位返回键 key 的剩余生存时间。
在Redis内部,每当我们设置一个键的过期时间时,Redis就会将该键带上过期时间存放到一个过期字典中。当我们查询一个键时,Redis便首先检查该键是否存在过期字典中,如果存在,那就获取其过期时间。然后将过期时间和当前系统时间进行比对,比系统时间大,那就没有过期;反之判定该键过期。
此外:
- 对于字符串类型键,执行set命令会去掉过期时间,这个问题很容易在开发中被忽视
- 如下是Redis源码中,set命令的函数setKey,可以看到最后执行了
removeExpire(db,key)
函数去掉了过期时间:
- 如下是Redis源码中,set命令的函数setKey,可以看到最后执行了
void setKey(redisDb *db, robj *key, robj *val) {
if (lookupKeyWrite(db,key) == NULL) {
dbAdd(db,key,val);
} else {
dbOverwrite(db,key,val);
}
incrRefCount(val);
// 去掉过期时间
removeExpire(db,key);
signalModifiedKey(db,key);
}
- Redis不支持二级数据结构(例如哈希、列表)内部元素的过期功能,例如不能对列表类型的一个元素做过期时间设置。
- setex命令作为set+expire的组合,不但是原子执行,同时减少了一次网络通讯的时间。
过期删除策略
通常删除某个key,我们有如下三种方式进行处理
1 定时删除
在设置某个key 的过期时间同时,我们创建一个定时器,让定时器在该过期时间到来时,立即执行对其进行删除的操作。
- 优点:定时删除对内存是最友好