Redis4.0版本增加了很多诱人的新特性,在redis精细化运营管理中都非常有用(猜想和antirez加入redislabs有很大关系);此系列几篇水文主要介绍以下几个新特性的使用和效果。
- Redis Memeory Command:详细分析内存使用情况,内存使用诊断,内存碎片回收;
- PSYNC2:解决failover和从实例重启不能部分同步;PSYNC3已经路上了;
- LazyFree: 再也不用怕big key的删除引起集群故障切换;
- LFU: 支持近似的LFU内存淘汰算法;
- Active Memory Defragmentation:内存碎片回收效果很好(实验阶段);
- Modules: Redis成为更多的可能(觉得像mongo/mysql引入engine的阶段);
- 因暂未有官方的详细文档,加之业余时间有限; 还请各位看官请轻拍。:)
那本文先介绍第一个特性memory指令。
Memory Command简介
redis4.0引入新的命令memory, memory命令共有5个子命令;
让我们能更深入要了解redis内部的内存使用情况。
通过memory help命令,可以查看除memory doctor的其他4个子命令;
5个指令简介如下:
- MEMORY USAGE [SAMPLES] -“Estimate memory usage of key”
- MEMORY STATS -“Show memory usage details”
- MEMORY PURGE -“Ask the allocator to release memory”
- MEMORY DOCTOR - “A better observability on the Redis memory usage.”
- MEMORY MALLOC-STATS - “Show allocator internal stats”
本文简述memory每个子命令的用途和部分实现。
1 memory usage
在redis4.0之前,只能通过DEBUG OBJECT命令估算key的内存使用(字段serializedlength),但因为相差太大,没有太多参考价值。
注:可以通过rdb工具分析rdb文件,获得某个key的实际使用内存
如以下示例,k1的序列化值是7。
memory usage的基本使用
usage子命令使用非常简单,直接按memory usage key名字;如果当前key存在,则返回key的value实际使用内存估算值;如果key不存在,则返回nil.
示例:
memory usage细节分析
memory usage不包含key串的内存占用
- memory usage不包含Key Expire的内存占用
- 对于集合的数据类型(除string外), usage子命令采用类似LRU SAMPLES的抽样方式,默认抽样5个元素求平均 X 元数个数 得出实际内存占用(下一节会详细说明)。所以计算是近似值,当面可以指定抽样的SAMPLES个数。
示例说明: 生成一个100w个字段的hash键:hkey, 每字段的value长度是从1~1024字节的随机值。
这是使用抽样求平均的算法,要想获取key较精确的内存值,就指定更大SAMPLES个数。但并不越大越好,因为越大,memory usage占用cpu时间分片就大。
- memory usage时间复杂度,和指定的SAMPLES数有点
见以下示例,SAMPLES为1000耗时0.176ms, 为100000耗时14.65ms
注:全实例的Expire内存占用,详见下文memory stats子命令的overhead.hashtable.expires)
memory usage源码实现
- memory命令的入口函数为memoryCommand(object.c文件中)
因为文章篇幅,这里只贴少部分代码:
2 memory stats
在redis 4.0之前,我们只能通过info memory查看redis实例的内存大体使用状况;而内存的使用细节,比如expire的消耗,client output buffer, query buffer等是很难直观显示的。 memory stats命令就是为展现redis内部内存使用细节。
memory stats的基本使用
memory stats命令直接运行,返回当前实例内存使用细节;命令的系统开销小(可用于监控采集)。示例如下: 运行时返回33行数据,16个子项目; 下节详细分析,每个子项目的具体含义。
memory stats细节分析
- peak.allocated: redis从启动来,allocator分配的内存峰值;同于info memory的used_memory_peak
- total.allocated: allocator分配当前内存字节数;同于info memory的used_memory
- startup.allocated: redis启动完成使用的内存字节数;- initial_memory_usage; / Bytes used after initialization. /
- replication.backlog: redis复制积压缓冲区(replication backlog)内存使用字节数; 通过repl-backlog-size参数设置,默认1M,上例中redis设置是100MB。(每个实例只有一个backlog)
注意:1. redis启用主从同步,不管backlog是否被填充,replication.backlog都等于repl-backlog-size的值。
笔者觉得此值应设置为repl_backlog_histlen更合适,没太明白大神的用意。
- slave也会启用backlog;用于slave被提升为master后,
仍能使用PSYNC(这也是redis4.0 PSYNC 2.0实现的基础
clients.slaves: 在master侧,所有slave clients消耗的内存字节数(非常重要的指标)。
每个slave连接master有且只有一个client, 标识为Sclient list命令中flag为S. 这里消耗的内存指每个slave client的query buffer, client output buffer和client本身结构体占用。
有此指标,就能有效监控和分析slave client消耗的output buffer, 更优化地设置”client-output-buffer-limit”。
下面示例:当slave client limit设置很大时,可见client的output占用内存非常大,clients.slaves已达3GB. 以前只能通过client list的omem字段分析。
- clients.normal:Redis所有常规客户端消耗内存节字数(非常重要)
即所有flag为N的客户端内存使用: query buffer + client output buffer + client的结构体内存占用。
计算方式和clients.slave类似。 这个子项对于我们监测异常的数据写入或读取的client非常有用。
- aof.buffer: AOF BUFFER使用内存字节数; 一般较小,只有出现AOF rewrite时buffer会变得较大。开启AOF或定期执行BGREWRITEAOF命令的业务,可能使用内存较大,需关注此项目。
- overhead.hashtable.main:
- overhead.hashtable.expires:
- overhead.total:redis额外的总开销内存字节数; 即分配器分配的总内存total.allocated,减去数据实际存储使用内存。overhead.total由7部分组成,公式如下:
- keys.count: 整个实例key的个数; 相同于dbsize返回值
- keys.bytes-per-key:每个key平均占用字节数;把overhead也均摊到每个key上。不能以此值来表示业务实际的key平均长度。
- dataset.bytes:表示redis数据占用的内存容量,即分配的内存总量,减去总的额外开销内存量。
- dataset.percentage:表示redis数据占用内存占总内存分配的百分比(重要);
可表示业务的redis数据存储的内存效率
- peak.percentage:当前内存使用量与峰值时的占比
- fragmentation: 表示Redis的内存碎片率(非常重要);前文的项目中都没包含redis内存碎片属性
/ Fragmentation = RSS / allocated-bytes /, 同于info memory中的mem_fragmentation_ratio
3 memory doctor
memory doctor命令分析redis使用内存的状态,根据一系列简单判断,给出一定的诊断建议,有一定参考价值。
memory doctor的基本使用
在redis-cli中运行memory doctor命令,如果内存使用有明显不合里的情况,会给出不合理的状态,同时给出处理的建议。
示例如下:
memory doctor细节分析
memory doctor主要列举条件判断,满足条件的给出检查结果和建议。
主要包含以下几点,满足其中一点,就给出诊断结果和建议:
- used_memory小于5M,doctor认为内存使用量过小,不做进一步诊断
- peak分配内存大于当前total_allocated的1.5倍,可能说明RSS远大于used_memory
- 内存碎片率大于1.4
- 每个Normal Client平均使用内存大于200KB
- 每个Slave Client平均使用内存大于10MB
memory doctor的源码实现
dockor命令的实现函数getMemoryDoctorReport(void)在object.c源文件
核心代码块如下:
4 memory purge
memory purge命令通过调用jemalloc内部命令,进行内存释放,尽量把redis进程占用但未有效使用内存,即常说的内存碎片释放给操作系统。
memory purge功能只适用于使用jemalloc作为allocator的实例。
redis的内存碎片率,是DBA比较头疼的事; 如某个业务下线删除了大量的key,redis不会把“清理”的内存及时归还给操作系统;但这部分内存可以被redis再次利用。
redis4.0提供两种机制解决内存碎片问题,一是memory purge命令; 二是Active memory defragmentation,目前还处于实验阶段,回收效率相当高; 本节只介绍memory purge.
memory purge的基本使用
memory purge使用简单,对性能没明显影响;通过测试验证来看,内存碎片回收的效率不高,当mem_fragmentation_ratio为2时,执行purge基本没有回收;
下面例子中:内存碎片率mem_fragmentation_ratio为8.2,执行memory purge, 碎片率下降为7.31,回收内存0.28GB。 从4.0版本来看,回收的效率不太理想。
memory purge命令只在jemalloc分配器中有效。
因真正释放内存操作,是通过jemalloc的底层实现,笔者没太看明白;
感兴趣的看官,阅读object.c源文件中的memoryCommand()函数逻辑代码如下:
此命令用于打印allocator内部的状态,目前只支持jemalloc。对于源码开发同学,应该比较有用;简单示例如下:
总结
memory命令使用我们能直观地查看redis内存分布,对我们掌握内存使用情况,有针对性地做业务的内存使用优化。尤其是purge, stats, usage三个子命令。 相信在新的版本中,memory命令的功能会更加强:)