Redis之所以性能强,最主要的原因就是基于内存存储,然而单节点的Redis其内存大小不宜过大,否则会影响持久化或主从同步的性能。
我们可以通过修改redis.conf配置文件来设置Redis的最大内存,配置项为maxmemory:
# 格式:
# maxmemory <bytes>
# 例如:
maxmemory 1gb
当内存使用达到上限,就无法存储更多数据了,因此,为了解决这个问题,Redis内部会有两套内存回收的策略:
查看某个key剩余的存活时间:TTL? key?
# 写入一条数据
set num 123
# 设置20秒过期时间
expire num 20
# 写入一条数据并设置20s过期时间
set num EX 20
Redis本身是一个典型的key-value的键值型内存存储数据库,因此所有的key-value都保存在Dict结构中,在其redisDb结构体中,有两个Dict,也就是哈希表:一个用来记录KEY-VALUE键值对(当然存的不是真正的Key-Value,存储的其实是RedisObject对应的内存地址的指针),另一个用来记录key的TTL。
过期字典存储在 redisDb 结构中,如下:?
来看下redisDb的底层源码:
typedef struct redisDb {
dict dict; / The keyspace for this DB , 也就是存放KEY和VALUE的哈希表*/
dict *expires; /* 同样是哈希表,但保存的是设置了TTL的KEY,及其到期时间*/
dict *blocking_keys; /* Keys with clients waiting for data (BLPOP)*/
dict *ready_keys; /* Blocked keys that received a PUSH */
dict *watched_keys; /* WATCHED keys for MULTI/EXEC CAS /
int id; / Database ID, 0 ~ 15 /
long long avg_ttl; / Average TTL, just for stats /
unsigned long expires_cursor; / Cursor of the active expire cycle. */
list *defrag_later; /* List of key names to attempt to defrag one by one, gradually. */
} redisDb;
每当我们对一个key设置了过期时间后,Redis会把该key带上过期时间存储到一个过期字典(expires dict)中,也就是说「过期字典」保存了数据库中所有 key 的过期时间。?
当我们查询一个 key 时,Redis 首先检查该 key 是否存在于过期字典中:
TTL(Time To Live)的含义是存活时间。?
惰性删除
周期删除或定期删除
优点:
- 因为每次访问时,才会检查该key是否过期,因此惰性删除策略可以节省CPU资源,对CPU时间最友好。
缺点:
- 如果一个key已经过期,而这个key又仍然保留在数据库中,那么只要这个过期key一致没有被访问,它所占用的内存就不会释放,会造成一定的内存空间浪费,所以惰性删除策略对内存不友好? ?=>? ?这不就是会导致内存泄漏吗???
Redis默认会每秒进行10次过期检查(此配置可以通过Redis的配置文件redis.conf进行配置,配置键为hz,它的默认值是hz 10),每次检查数据库并不是遍历过期字典中的所有key,而是从数据库中随机抽取一定数量的key进行过期检查:
可以看到,定期删除是一个循环的流程。
Redis为了保证定期删除不会出现循环过度,导致线程卡死现象,为此增加了定期删除循环流程的时间上限,默认不会超过25ms,超出时间限制则退出。?
优点:
- 定期删除是Redis的主动删除策略,它可以确保过期key能够及时被删除
缺点:
- 会占用CPU资源去扫描key,可能会影响到Redis的性能?
可以看到,惰性删除策略和定期删除策略都有各自的优缺点,所以Redis选择「惰性删除+定期删除」这两种策略配和使用,以求在合理使用 CPU 时间和避免内存浪费之间取得平衡。?
如果过期键没有被访问,而定期删除又跟不上新键产生的速度,内存不就慢慢耗尽了吗??
当内存达到阈值时执行内存淘汰,但问题是Redis什么时候会去判断内存是否达到阈值呢?
Redis支持内存淘汰,配置参数maxmemory_policy决定了内存淘汰策略,这个参数一共有8个枚举值,也就是说Redis内存淘汰策略共有8种,这八种策略大体分为「不进行数据淘汰」和「进行数据淘汰」两类策略:
1. 不进行数据淘汰的策略
2. 进行数据淘汰的策略
针对「进行数据淘汰」这一类策略,又可以细分为「在设置了过期时间的数据中进行淘汰」和「在所有数据范围内进行淘汰」这两类策略。 在设置了过期时间的数据中进行淘汰:
在所有数据范围内进行淘汰:
当Redis作为缓存使用的时候,推荐使用allkeys-lru淘汰策略,该策略会将最近最久未使用的key淘汰,像这种key后期命中的概率也最低,所以将其淘汰。?
?