redis--内存模型

一、简介

Redis 所有的数据都存在内存中,当前内存虽然越来越便宜,但跟廉价的硬盘相比成本还是比较昂贵,因此如何高效利用Redis 内存变得非常重要
 
介绍Redis内存之前首先说明如何统计Redis使用内存的情况。

二、内存消耗划分

Redis 进程内消耗主要包括:自身内存 + 对象内存 + 缓冲内存 + 内存碎片, 其中Redis 空进程自身内存消耗非常少,通常 used_memory_rss 3MB 左右,used_memory在 800KB 左右,一个空的 Redis 进程消耗内存可以忽略不计。
 

1.对象内存

作为数据库,数据是最主要的部分;这部分占用的内存会统计在used_memory中。Redis使用键值对存储数据,其中的值(对象)包括5种类型,即字符串、哈希、列表、集合、有序集合。这5种类型是Redis对外提供的,实际上,在Redis内部,每种类型可能有2种或更多的内部编码实现;这些在之前的文章中有详解过https://blog.csdn.net/zhang09090606/article/details/115194101

2.缓冲内存

缓冲内存主要包括:客户端缓冲、复制积压缓冲区、 AOF 缓冲区。
客户端缓冲指的是所有接入到Redis 服务器 TCP 连接的输入输出缓冲。 输入缓冲无法控制,最大空间为1G ,如果超过将断开连接
复制积压缓冲区之前介绍主从复制时介绍过 https://blog.csdn.net/zhang09090606/article/details/115932066
AOF缓冲区在之前的持久化一章介绍过 https://blog.csdn.net/zhang09090606/article/details/115825083
 

3.内存碎片

Redis默认的内存分配器采用jemalloc,可选的分配器还有:glibc、 tcmalloc。内存分配器为了更好地管理和重复利用内存,分配内存策略一般 采用固定范围的内存块进行分配。例如jemalloc64位系统中将内存空间划 分为:小、大、巨大三个范围。每个范围内又划分为多个小的内存块单位比如当保存5KB对象时jemalloc可能会采用8KB的块存储,而剩下的3KB空间变为了内存碎片不能再分配给其他对象存储。内存碎片问题虽然是所有内存服务的通病,但是jemalloc针对碎片化问题专门做了优化,一般不会存在过度碎片化的问题,正常的碎片率(mem_fragmentation_ratio)在1.03左右。但是当存储的数据长短差异较大时,

·小:[8byte][16byte32byte48byte...128byte][192byte,256byte,...512byte][768byte1024byte...3840byte]
·大:
[4KB8KB12KB...4072KB]
·巨大:
[4MB8MB12MB...]

以下场景容易出现高内存碎片问题:

(1)频繁做更新操作,例如频繁对已存在的键执行 append setrange 等更新操作。
(2)大量过期键删除,键对象过期删除后,释放的空间无法得到充分利用,导致碎片率上升
 
解决方案:
(1)数据对齐: 在条件允许的情况下尽量做数据对齐,比如数据尽量采用 数字类型或者固定长度字符串等,但是这要视具体的业务而定,有些场景无 法做到。
(2)安全重启: 重启节点可以做到内存碎片重新整理,因此可以利用高可 用架构,如Sentinel Cluster ,将碎片率过高的主节点转换为从节点,进行安全重启
 

三、内存管理

Redis 使用 maxmemory 参数限制最大可用内存。限制内存的目的主要有:
(1)用于缓存场景,当超出内存上限 maxmemory 时使用 LRU 等删除策略释放空间。
(2)防止所用内存超过服务器物理内存。
 
Redis 的内存上限可以通过 config set maxmemory进行动态修改,即修改最大可用内存,例如当发现Redis-2没有做好内存预估,实际只用了不到2GB内存,而Redis-1实例需要扩容到6GB内存才够用,这时可以分别执行如下命令进行调整

四、内存回收策略

Redis 的内存回收机制主要体现在以下两个方面:
(1)删除到达过期时间的键对象。
(2)内存使用达到 maxmemory 上限时触发内存溢出控制策略
 

1.删除过期键对象

Redis 所有的键都可以设置过期属性,内部保存在过期字典中。由于进 程内保存大量的键,维护每个键精准的过期删除机制会导致消耗大量的 CPU,对于单线程的 Redis 来说成本过高,因此 Redis 采用惰性删除和定时任 务删除机制实现过期键的内存回收
 
惰性删除:
用于当客户端读取带有超时属性的键时,如果已经超过键设置的过期时间,会执行删除操作并返回空,这种策略是出于节省 CPU成本考虑,不需要单独维护 TTL 链表来处理过期键的删除。但是单独用 这种方式存在内存泄露的问题,当过期键一直没有访问将无法得到及时删 除,从而导致内存不能及时释放。正因为如此,Redis 还提供另一种定时任 务删除机制作为惰性删除的补充。


定时任务删除:
Redis内部维护一个定时任务,默认每秒运行10次(通过配置hz控制)。定时任务中删除过期键逻辑采用了自适应算法,根据键的 过期比例、使用快慢两种速率模式回收键

 

如果之前回收键逻辑超时,则在 Redis 触发内部事件之前再次以快模 式运行回收过期键任务,快模式下超时时间为1 毫秒且 2 秒内只能运行 1 次。 快慢两种模式内部删除逻辑相同,只是执行的超时时间不同。
 

2.内存溢出控制策略

Redis 所用内存达到 maxmemory 上限时会触发相应的溢出控制策略。 具体策略受maxmemory-policy 参数控制, Redis 支持 6 种策略
 
(1)noeviction :默认策略,不会删除任何数据,拒绝所有写入操作并返 回客户端错误信息(error OOM command not allowed when used memory ,此 时Redis 只响应读操作。
(2)volatile-lru :根据 LRU 算法删除设置了超时属性( expire )的键,直到腾出足够空间为止。如果没有可删除的键对象,回退到noeviction 策略。
(3)allkeys-lru :根据 LRU 算法删除键,不管数据有没有设置超时属性, 直到腾出足够空间为止。
(4)allkeys-random :随机删除所有键,直到腾出足够空间为止。
(5)volatile-random :随机删除过期键,直到腾出足够空间为止。
(6)volatile-ttl :根据键值对象的 ttl 属性,删除最近将要过期数据。如果没有,回退到noeviction 策略。
 
频繁执行回收内存成本很高,主要包括查找可回收 键和删除键的开销,如果当前Redis 有从节点,回收内存操作对应的删除命令会同步到从节点,导致写放大的问题

版权声明:本文为zhang09090606原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。