概述
在使用CacheCloud时看到一个碎片率的监控指标,于是有了此文。
简介
内存碎片率,memory fragmentation ratio,MFR,Redis的一个性能指标,其计算公式:MFR=used_memory_rss/used_memory
无论是通过redis-cli
还是RDM工具,输入info
或更具体的info memory
命令,即可得到如下信息:
used_memory:15288560
used_memory_human:14.58M
used_memory_rss:14237696
used_memory_rss_human:13.58M
mem_fragmentation_ratio:0.93
mem_allocator:jemalloc-3.6.0
输出参数:
- used_memory
已使用的内存大小,包括redis进程内部开销和cache的数据所占用的内存,单位byte;human表示可读数据,单位Mb,即除以2^20 - used_memory_rss
rss,Resident Set Size,表示redis物理内存的大小,即向OS申请多少内存使用 - mem_fragmentation_ratio:
- 小于1时,表示发生swap,正在使用虚拟内存,即可用内存不够,虚拟内存其实就是硬盘,性能比内存低得多,应该增强机器的内存以提高性能
- 内存碎片率略高于1属于正常,一般超出1.5(待确认?)就说明redis的内存管理变差
- 远大于1时,表示分配的内存超过实际使用的内存;数值越大,碎片率越严重
- mem_allocator:
指定编译时Redis使用的内存分配器:libc、jemalloc、tcmalloc,默认是jemalloc。jemalloc在64位系统中,将内存空间划分为小、大、巨大三个范围;每个范围内又划分许多小的内存块单位;存储数据的时候,会选择大小最合适的内存块进行存储。
另外,当内存使用量(used_memory)很小的时候,这个值参考价值不大。所以,建议used_memory至少1G以上才考虑对内存碎片率进行监控。
碎片整理允许Redis压缩内存空间,从而回收内存。
原因
分析实际环境,因为该redis主要是存储频繁更新的数据,每次更新数据之前,redis会删除旧的数据,实际上,由于Redis释放了内存块,但内存分配器并没有返回内存给操作系统,这个内存分配器是在编译时指定的,可以是libc、jemalloc或者tcmalloc。used_memory_rss会越来越大,导致MFR越来越高。
当Redis中清理大量的Key之后原先Redis申请的内存(used_memory_rss)将继续持有而不会释放,此时查看内存信息将会看到存在大量的内存碎片。
redis产生内存碎片有两个原因,
- redis自身的内存分配器。
- 修改cache的值,且修改后的value与原来value的大小差异较大。
进程需要用内存的话,会先通过OS向device申请,然后才能够使用。一般进程在不需要使用的时候,会释放掉这部分内存并返回给device。但是redis作者可能为了更高的性能,所以在redis中实现了自己的内存分配器来管理内存,不会马上返还内存,不用每次都向OS申请了,从而实现高性能。
但是,在内存分配器的那张图片我们知道,redis的每个k-v对初始化的内存大小是最适合的,当这个value改变的并且原来内存大小不适用的时候,就需要重新分配内存了。(但是value存比原来小不知道会不会产生碎片)。重新分配之后,就会有一部分内存redis无法正常回收,一直占用着。
解决方法
- Redis 4 以前的版本,重启Redis服务器可以让额外产生的内存碎片失效并重新作为新内存来使用,使操作系统恢复高效的内存管理;
- Redis 4+ 版本,
- 修改内存分配器。Redis支持glibc’s malloc、jemalloc11、tcmalloc几种不同的内存分配器,每个分配器在内存分配和碎片上都有不同的实现。不建议普通管理员修改Redis默认内存分配器,因为这需要完全理解这几种内存分配器的差异,也要重新编译Redis。
Redis4版本之后开始支持内存碎片的清理,默认情况下自动清理碎片的参数是关闭的:config get activedefrag
应该得到no的输出,开启:config set activedefrag yes
,再次输入config get activedefrag
验证输出是否为yes。
开启后,后台会启动进程,也可以手动清理:memory purge
查看:memory malloc-stats
注:此命令自版本可用。
TODO:输出解读。。
参数
相关参数配置说明
# Enabled active defragmentation
# 碎片整理总开关
# activedefrag yes
# Minimum amount of fragmentation waste to start active defrag
# 内存碎片达到多少的时候开启整理
active-defrag-ignore-bytes 100mb
# Minimum percentage of fragmentation to start active defrag
# 碎片率达到百分之多少开启整理
active-defrag-threshold-lower 10
# Maximum percentage of fragmentation at which we use maximum effort
# 碎片率小余多少百分比开启整理
active-defrag-threshold-upper 100
# Minimal effort for defrag in CPU percentage
active-defrag-cycle-min 25
# Maximal effort for defrag in CPU percentage
active-defrag-cycle-max 75
源码
分配器
jemalloc
Redis 3.2.9 (整个3版本?)的默认分配器
tcmalloc
glibc’s malloc
即libc
参考
Redis 4.0 自动内存碎片整理(Active Defrag)源码分析Inside of Jemalloc
Redis性能问题排查解决手册(七)