Redis内存管理模型
内存资源有限且珍贵,Redis作为高性能内存数据库,充分有效利用内存是充分且必要的,下面简单来看下Redis的内存管理模型。
过期机制
内存是有限的,所以需要将不被使用或者偶尔被使用的数据移出内存,以提升内存的有效利用率。Redis提供了过期机制来实现这个功能,即在创建key的时候可以指定过期时间,如果key已存在可以通过expire命令设置过期时间。
如果不指定过期时间,则永不过期

那Redis是如何判断数据过期的呢?redisDB,可以看作是一个hash数据结构,在处理请求之前,先查询redisDB验证数据是否存在或是否过期,如果存在且在有效时间内再执行真正的访问。如果发现过期就删除数据。
过期删除机制
常见的过期删除机制有以下四个:
- 定时删除:为每个键配置一个定时器,过期立即删除,CPU压力很大
- 定期删除:每隔一段时间选取若干key,删除其中的过期key,对内存友好
- 惰性删除:在命中key时,如果过期则删除,对CPU友好
- 延迟队列:把设置过期时间的 key 放到一个延迟队列里,到期之后就删除 key。延迟队列的维护开销比较大,而且本身占用很高内存
Redis采用的是惰性删除+定期删除的策略,平衡CPU和内存的压力,其他两个成本太高
注意,定期删除会随机选取N个元素,这N个元素并不保证都是过期key,N通过 ACTIVE_EXPIRE_CYCLE_KEYS_PER_LOOP参数配置,默认是20
定期删除的执行间隔通过hz参数配置,默认为10,此外还提供了dynamic hz功能,无需配置,redis会动态地计算这个hz值,动态hz是默认的使用策略
// 默认为 10
hz 10
// 默认开启
dynamic-hz yes
内存淘汰机制
虽然过期机制能够尽量避免内存占用过高,但是在特定场景下,机器内存也会不可避免地被耗尽,需要根据特定的内存淘汰算法淘汰部分key
Redis 的内存淘汰策略只有在运行内存达到了配置的最大内存阈值时才会触发,这个阈值是通过 redis.conf 的 maxmemory 参数来定义的,默认为0,表示不限制最大内存阈值

configEnum maxmemory_policy_enum[] = {
{"volatile-lru", MAXMEMORY_VOLATILE_LRU}, // 根据LRU从设置过期时间的key中淘汰
{"volatile-lfu", MAXMEMORY_VOLATILE_LFU}, // 根据LFU从设置过期时间的key中淘汰
{"volatile-random",MAXMEMORY_VOLATILE_RANDOM}, // 随机从设置过期时间的key中淘汰
{"volatile-ttl",MAXMEMORY_VOLATILE_TTL}, // 根据到期时间从设置过期时间的key中淘汰
{"allkeys-lru",MAXMEMORY_ALLKEYS_LRU}, // 根据LRU从所有key中淘汰
{"allkeys-lfu",MAXMEMORY_ALLKEYS_LFU}, // 根据LFU从所有key中淘汰
{"allkeys-random",MAXMEMORY_ALLKEYS_RANDOM}, // 随机从所有key中淘汰
{"noeviction",MAXMEMORY_NO_EVICTION}, // 内存满抛异常,默认策略
};
noeviction是默认的内存淘汰策略,即内存满后抛异常
Redis LRU算法实现:https://www.cnblogs.com/vivotech/p/17531827.html
这里简单介绍下Redis的LRU实现:
- Redis的LRU算法实际上并未维护一个链表存储元素的访问顺序,而是采用了一种简化方案:进行内存淘汰时,随机选取maxmemory-samples个键值对,通过redisObject对象的lru字段(记录了最近一次访问的时间戳),选择该字段最小的键值对淘汰掉
- LRU的弊端在于不适合冷热数据,一份数据在之前可能累积了很大的请求量,但是最近一直没有被访问,但一直驻留在内存,可以通过LFU算法解决,结合了访问次数和存活时间来计算key的热度
内存碎片管理机制

内存碎片是怎么来的呢?
- Redis采用空间预分配机制,申请的内存一般要比实际使用的要多
- 过期机制,数据的过期时间不同,进一步加剧了内存的碎片化
Redis可以实时计算内存碎片率,可以配置碎片内存大小和内存碎片率阈值来自动触发内存整理
// 内存碎片占用空间达到 500mb 的时候开始清理
config set active-defrag-ignore-bytes 500mb
// 内存碎片率大于 1.5 的时候开始清理
config set active-defrag-threshold-lower 50
redis4.0之前是同步阻塞整理内存,无法响应外部客户端请求;4.0之后引入了渐进式内存整理,每次只整理部分内存,可以持续响应外部请求
如果碎片率过大,同时碎片整理速度很慢导致请求时延持续下降的话,可以通过重启节点来缓解,重启节点会自动整理全部内存

浙公网安备 33010602011771号