文章目录
Redis内存和数据淘汰策略一、命令和配置1.1 命令1.2 配置1.3 修改配置
二、内存分析2.1 参数分析2.2 内存占用分析
三、内存优化3.1 redisObject对象3.2 缩短k-v3.3 共享对象池3.4 字符串优化3.5 编码优化3.6 减少键的数量
四、参考
Redis内存和数据淘汰策略
下面是基于Redis 4.0.13版本,不同版本可以略有差异。
一、命令和配置
1.1 命令
info memory。该命令可以输出Redis内存相关信息。(info可以输出全部信息)
192.168.13.52:6379
>
192.168.13.52:6379
> info memory
used_memory:1917336
used_memory_human:1.83M
used_memory_rss:11747328
used_memory_rss_human:11.20M
used_memory_peak:1918312
used_memory_peak_human:1.83M
used_memory_peak_perc:99.95%
used_memory_overhead:1901848
used_memory_startup:786696
used_memory_dataset:15488
used_memory_dataset_perc:1.37%
total_system_memory:135071277056
total_system_memory_human:125.79G
used_memory_lua:37888
used_memory_lua_human:37.00K
maxmemory:0
maxmemory_human:0B
maxmemory_policy:noeviction
mem_fragmentation_ratio:6.13
mem_allocator:jemalloc-4.0.3
active_defrag_running:0
lazyfree_pending_objects:0
参数含义如下:
输出参数含义
used_memory由Redis分配器分配的内存总量,包含了redis进程内部开销和数据占用的内存,以字节(byte)为单位used_memory_human可读展示used_memoryused_memory_rss向操作系统申请的内存大小。与 top 、 ps等命令的输出一致used_memory_rss_human可读展示used_memory_rssused_memory_peakredis的内存消耗峰值(以字节为单位)used_memory_peak_human可读展示used_memory_peakused_memory_peak_perc使用内存达到峰值内存的百分比,(used_memory/ used_memory_peak) *100%used_memory_overheadRedis为了维护数据集的内部机制所需的内存开销,包括所有客户端输出缓冲区、查询缓冲区、AOF重写缓冲区和主从复制的backlogused_memory_startupRedis服务器启动时消耗的内存used_memory_dataset数据占用的内存大小,即used_memory-used_memory_overheadused_memory_dataset_perc数据占用的内存大小的百分比(used_memory_dataset/(used_memory-used_memory_startup))*100%total_system_memory整个系统内存total_system_memory_human可读展示total_system_memoryused_memory_luaLua脚本存储占用的内存used_memory_lua_human可读展示Lua脚本存储占用的内存maxmemoryRedis实例的最大内存配置maxmemory_human可读展示Redis实例的最大内存配置maxmemory_policy当达到maxmemory时的淘汰策略mem_fragmentation_ratio碎片率,used_memory_rss/ used_memorymem_allocator内存分配器active_defrag_running表示是否有内存碎片整理活动 0表示没有,1表示有lazyfree_pending_objects0表示不存在延迟释放的挂起对象
1.2 配置
阅读redis.conf配置文件,找到MEMORY MANAGEMENT部分,可以看到关于内存管理的配置,默认都没有放开,主要配置是maxmemory,maxmemory-policy和maxmemory-samples。
配置解析默认值配置示例
maxmemory最大内存限制0,表示不限制maxmemory 1GB/maxmemory 100MBmaxmemory-policy内存满后淘汰策略noevictionvolatile-lrumaxmemory-samples淘汰算法样本55
maxmemory限制的是info mempry中used_memory统计的值,因此Redis实际使用的内存值是可以超过maxmemory限制的内存值的。
1.3 修改配置
方式一:修改配置文件,重启实例方式二:没有禁用config的情况下,使用config修改
192.168.13.53:6379
> config
set maxmemory 500MB
192.168.13.53:6379
> info memory
maxmemory:524288000
maxmemory_human:500.00M
192.168.13.53:6379
> config get maxmemory
1
) "maxmemory"
2
) "524288000"
192.168.13.53:6379
> config rewrite
OK
192.168.13.53:6379
>
//配置文件已经同步修改,追加在配置文件的最后
intellif@segment2:/opt/redis/redis-4.0.13$
tail -fn 2./redis.conf
maxmemory 500mb
二、内存分析
2.1 参数分析
根据info memory的输出,我们通常可以简单的分析。
输出参数含义
used_memory由Redis分配器分配的内存总量,包含了redis进程内部开销和数据占用的内存,以字节(byte)为单位used_memory_rss向操作系统申请的内存大小。与 top 、 ps等命令的输出一致mem_fragmentation_ratio碎片率,used_memory_rss/ used_memory
当mem_fragmentation_ratio稍大于1时,是比较合理的,表示内存碎片率比较低,如果大于1.5说明Redis使用了实际需要内存的1.5倍,碎片接近50%,可以考虑稍微优化。当mem_fragmentation_ratio稍小于1时,需要的内存大于分配的内存,说明OS存在内存交换,可能会引起明显的延迟。
2.2 内存占用分析
内存占用描述备注
Redis数据占用内存K-V对占用内存used_memory_dataset和used_memory_dataset_perc查看数据内存大小和占比Redis进程其他开销客户端输出缓冲区、查询缓冲区、AOF重写缓冲区和主从复制的backlogused_memory_overhead查看该部分大小Redis进程其他开销如:Lua脚本存储占用的内存,子进程开销等used_memory_lua内存碎片操作系统分配了但是Redis无法利用(碎片会导致mem_fragmentation_ratio>1)
三、内存优化
3.1 redisObject对象
Redis中五大数据类型都是使用redisObject来封装的,在其中加入了type、编码、LRU、等信息。如果对象是String且长度<=39,则只需要进行一次内存操作,过大则会增加内存分配的次数。
//可以清理空闲的key,Object命令可以查看redisObject对象的内部属性,比如idletime,refcount等
192.168.13.53:6379
> set key1 val1
OK
192.168.13.53:6379
> OBJECT idletime key1
(integer
) 5
192.168.13.53:6379
> OBJECT idletime key1
(integer
) 6
3.2 缩短k-v
设计上考虑尽量缩短key的长度可以考虑使用压缩算法,减小值占用空间
3.3 共享对象池
Redis内部对于0-9999的整数维护了整数对象池,(因为创建一个redisObject保存一个整数的开销比一个整数自身还要大)。在可以的情况下尽量使用整数对象,可以节约内存。
PS:共享对象池和与maxmemory+LRU策略冲突,因为在使用了maxmemory+LRU的策略时,Redis需要统计每个key的LRU并保存到redisObject的LRU字段,此时如果对象共享那么该字段也会共享,这样就无法做LRU淘汰,因此该模式下会禁用共享对象池
如果没有设置maxmemory,那么共享对象池是可以使用的,因为此时Redis不会做任何内存限制直到内存用尽,因此共享对象池可以正常工作。
另外只将整数做共享对象池也是性能和开销的折中,其他类型的判断相等复杂度是O
(n
)甚至O
(N2
),整数是O
(1
)。
3.4 字符串优化
避免字符串的频繁修改操作,而是直接使用set修改字符串,修改可能会因为预分配机制造成空间的浪费和内存碎片类似于json这样的键值对可以考虑采用hash存储,效率更高,获取时也支持hmget/hmset,而不是整体存取,性能更好(使用ziplist编码方式)
3.5 编码优化
//查看键值对的内部编码
192.168.13.53:6379
> set addr shenzhen
OK
192.168.13.53:6379
> OBJECT encoding addr
"embstr"
192.168.13.53:6379
>
//查看命令平均耗时:info Commandstats
关于编码优化这一块,有很多内容,详情阅读参考文章[3]的8.3小节
3.6 减少键的数量
比如100万个k-v对,可以考虑在业务上控制,使用1000个Hash存储,每一个Hash存储1000个k-v对,这样减少了外层键的数量可以节约内存,具体方法可以考虑散列,或者对key做一些拆分来实现。Hash使用ziplist编码,但是需要将长度控制在1000以内。
ziplist是连续内存结构,比较节约内存,适合存储小对象,查询复杂度是O
(N2
),因为存储数据比较短,所以性能能够满足。
四、参考
[1] Redis性能问题排查解决手册[2] Redis学习-内存优化[3] Redis开发与运维