好好整理了一下关于reids内存优化的知识,总算对redis内存管理有了一个初步的认识。
一.内存消耗分析
Redis进程内消耗主要包括:自身内存+对象内存+缓冲内存+内存碎片,其中Redis空进程自身内存消耗非常少,通常used_memory_rss在3MB左右,used_memory在800KB左右,一个空的Redis进程消耗内存可以忽略不计。
1.对象内存
数据都采用key-value数据类型,每次创建键值对时,至少创建两个类型对象:key对象和value对象。对象内存消耗可以简单理解为sizeof(keys)+sizeof(values)。键对象都是字符串,在使用Redis时很容易忽略键对内存消耗的影响,应当避免使用过长的键。
2.缓冲内存
缓冲内存主要包括:客户端缓冲、复制积压缓冲区、AOF缓冲区。
3.内存碎片
比如当保存5KB对象时jemalloc可能会采用8KB的块存储,而剩下的3KB空间变为了内存碎片不能再分配给其他对象存储。内存碎片问题虽然是所有内存服务的通病,但是jemalloc针对碎片化问题专门做了优化,一般不会存在过度碎片化的问题,正常的碎片率(mem_fragmentation_ratio)在1.03左右。
4.子进程内存消耗问题
Redis产生的子进程并不需要消耗1倍的父进程内存,实际消耗根据期间写入命令量决定,但是依然要预留出一些内存防止溢出。
二.内存管理
1.限制内存使用上限
2.内存回收策略
Redis的内存回收机制主要体现在以下两个方面:
· 删除到达过期时间的键对象。
· 内存使用达到maxmemory上限时触发内存溢出控制策略。
3.删除过期的健
Redis所有的键都可以设置过期属性,内部保存在过期字典中。由于进程内保存大量的键,维护每个键精准的过期删除机制会导致消耗大量的CPU,对于单线程的Redi来说成本过高,因此Redis采用惰性删除和定时任务删除机制实现过期键的内存回收。
惰性删除:
惰性删除:惰性删除用于当客户端读取带有超时属性的键时,如果已经超过键设置的过期时间,会执行删除操作并返回空,这种策略是出于节省CPU成本考虑,不需要单独维护TTL链表来处理过期键的删除。但是单独用这种方式存在内存泄露的问题,当过期键一直没有访问将无法得到及时删除,从而导致内存不能及时释放。
正因为如此,Redis还提供另一种定时任务删除机制作为惰性删除的补充。
定时任务删除:Redis内部维护一个定时任务,默认每秒运行10次(通过配置hz控制)。定时任务中删除过期键逻辑采用了自适应算法,根据键的过期比例、使用快慢两种速率模式回收键。
4.定时删除key策略
流程说明:
1)定时任务在每个数据库空间随机检查20个键,当发现过期时删除对应的键。
2)如果超过检查数25%的键过期,循环执行回收逻辑直到不足25%或运行超时为止,慢模式下超时时间为25毫秒。
3)如果之前回收键逻辑超时,则在Redis触发内部事件之前再次以快模式运行回收过期键任务,快模式下超时时间为1毫秒且2秒内只能运行1次。
三.内存优化
1.redisObject
2.共享内存池
共享对象池是指Redis内部维护[0-9999]的整数对象池。无论使用多少次0-9999中的整数,只占用一份0-9999的内存。
3.hash结构内存优化与关键点
同样的数据使用ziplist编码的hash类型存储比string类型节约内存。
·节省内存量随着value空间的减少越来越明显。
·hash-ziplist类型比string类型写入耗时,但随着value空间的减少,耗时逐渐降低。
4.hash结构健的设计
·1)当键离散度较高时,可以按字符串位截取,把后三位作为哈希的field,之前部分作为哈希的键。如:key=1948480哈希key=group:hash:1948,哈希field=480。
2)当键离散度较低时,可以使用哈希算法打散键,如:使用crc32(key)&10000函数把所有的键映射到“0-9999”整数范围内,哈希field存储键的原始值。
5.使用hash结构需要提前规避的问题
·客户端需要预估键的规模并设计hash分组规则,加重客户端开发成本。
·hash重构后所有的键无法再使用超时(expire)和LRU淘汰机制自动删除,需要手动维护删除。
·对于大对象,如1KB以上的对象,使用hash-ziplist结构控制键数量反而得不偿失。
不过瑕不掩瑜,对于大量小对象的存储场景,非常适合使用ziplist编码的hash类型控制键的规模来降低内存。
使用ziplist+hash优化keys后,如果想使用超时删除功能,开发人员可以存储每个对象写入的时间,再通过定时任务使用hscan命令扫描数据,找出hash内超时的数据项删除即可。
总结:
1)Redis实际内存消耗主要包括:键值对象、缓冲区内存、内存碎片。
2)通过调整maxmemory控制Redis最大可用内存。当内存使用超出时,根据maxmemory-policy控制内存回收策略。
3)内存是相对宝贵的资源,通过合理的优化可以有效地降低内存的使用量,内存优化的思路包括:
·精简键值对大小,键值字面量精简,使用高效二进制序列化工具。
·使用对象共享池优化小整数对象。 ·数据优先使用整数,比字符串类型更节省空间。
·优化字符串使用,避免预分配造成的内存浪费。
·使用ziplist压缩编码优化hash、list等结构,注重效率和空间的平衡。 ·使用intset编码优化整数集合。
·使用ziplist编码的hash结构降低小对象链规模。