Redis自身使用内存的统计数据,可通过执行info memory命令获取内存相关指标
info_memory详细解释
属性名 | 属性说明 |
---|---|
user_memory | Redis分配的内存总量,也就是内存存储的所有数据内存占用量 |
userd_memory_human | 以可读的格式返回user_memory |
user_memory_rss | 以操作系统的角度显示Redis进程占用的物理内存空间总量 |
user_memory_peak | 内存使用的最大值,表示user_memory的峰值 |
user_memory_peak_human | 以可读的格式返回user_memory_peak |
user_memory_lua | lua引擎所消耗的内存大小 |
mem_fragmentaton_ratio | user_memory_rss/user_memory比值,表示内存的碎片率 |
mem_allocator | /Redis使用的内存分配器 |
需要重点关注的指标是:user_memory_rss,user_memory,mem_fragmentaton_ratio
当mem_fragmentaton_ratio>1时,说明user_memory_rss-user_memory多出的部分内存并没有用于数据存储,而是被内存碎片所消耗,如果比值比较大,说明碎片化严重
当mem_fragmentaton_ratio<1,说明操作系统把Redis内存交换到硬盘导致,出现这种情况要重点注意,硬盘处理速度远远慢于内存,Redis性能会急剧变差,甚至僵死。
Redis进程内存消耗主要包括
Redis空进程自身消耗内存很少,通常user_memory_rss在3M左右,user_memory在800k左右,一个空的redis进程消耗的内存可以忽略不计
对象内存是Redis中占用内存最大的一块,存储着用户所有的数据。Redis所有的数据都是采用key-value数据类型,一个键包含两个对象:key对象和value对象。
对象的内存可以简单理解为:sizeof(keys)+sizeof(value)
缓存内存主要包括:客户端缓冲,复制积压缓存区和AOF缓存区
客户端缓冲是指所有接入到Redis服务器TCP连接的输入输出缓冲。输入缓冲无法控制,输出缓冲通过参数client-output-buffer-limit控制。客户端缓冲类型有如下三种:
Redis默认采用的内存分配方式是jemalloc,可选的分配器还有glibc,tcmalloc。例如我们可能保存5KB对象时,采用8KB的块存储,而剩下的3KB内存变成了内存碎片不能再分配给其他对象使用。
容易产生内存碎片场景
子进程内存消耗主要指执行AOF/RDB重写时,Redis创建的子进程内存消耗。Redis执行fork操作产生的子进程内存占用量对外表现为与父进程相同。Linux具有写时复制技术,父子进程共享相同的物理内存页,当父进程处理写请求时会对需要修改的页复制出一份副本完成写操作,而子进程依然读取fork时整个父进程的内存快照
Redis主要通过控制内存上限和回收策略来实现内存管理
Redis使用maxmemory参数限制最大可用内存,限制内存两个目的:
Redis的内存上限可以通过config set maxmemory进行动态设置
动态修改maxmemory,可以实现在当前服务器下动态伸缩Redis内存的目的
Redis内存回收机制主要体现在两个方面
Redis所有的键都可以设置过期属性,内存保存在过期字典中。
Redis采用惰性删除和定时任务删除机制来实现过期键的内存回收
当Redis所使用内存达到maxmemeory上限时会触发相应的溢出内存策略。
Redis支持6种策略
Redis存储的所有值对象在内存中定义为redisObject结构体
type字段:表示当前对象使用的数据类型
encoding字段:表示Redis内部编码类型
lru字段:记录对象最后一次被访问的时间
refcount字段:记录当前对象,yu’o’g’n被引用的次数,用于通过引用次数回收内存,当refcount=0可以安全回收当前对象空间
*prf字段:与对象的数据内容有关,如果是整数,直接存储数据;否则表示指向数据的指针
降低Redis内存使用最直接的方式就是缩减键和值的长度
key长度:键值越短越好,可以采用单词缩写
value长度:精简业务对象,去除不必要字段,可以采用压缩算法压缩空间
共享对象池是指Redis内存维护的整数对象池,用于节约内存。因此在开发满座条件下尽量使用整数对象以节约内存
但是当设置maxmemory并开启LRU相关淘汰策略时,Redis禁止使用共享内存对象池
为什么开启maxmemeory和LRU淘汰策略后对象池失效?
为什么只有整数对象池?
字符串对象是Redis内部最常用的数据类型。所有的键都是字符串类型,之对象除了整数之外都是使用字符串存储。
Redis自身实现的字符串结构特点:
字符串之所以采用预分配方式是为了防止修改操作不断重分配内存和字节数据拷贝。
字符串重构,指不一定把每份数据作为字符串整体存储,像json这样的数据可以使用hash结构,使用二级结构存储也能帮助我们节省内存。
Redis针对每种数据结构可以采用至少两种编码方式来实现
Redis为什么对一种数据类型实现多种编码方式?
存储相同的数据内容利用Redis的数据结构降低外层键的数量,也可以节省内存。