最近查看了一下redis运行状况,发现公司测试服务器的redis内存不太够用,但是实际占用内存的数据量其实不大,以前也没有这种情况,之前在cache层新增了一个防刷积分任务的逻辑才会这样,搜索一下原因,发现原来是产生了大量的内存碎片。
首先,查看redis的内存状态,要用info memory指令
ps:(这个是我flushdb后的结果,反面教材来的。。。)
图中几个参数的意义:
1、used_memory:
已经使用了的内存大小,包括redis进程内部开销和你的cache的数据所占用的内存,单位byte。
2、used_memory_human:
用户数据所占用的内存,就是你缓存的数据的大小。
3、used_memory_rss:(rss for Resident Set Size)
表示redis物理内存的大小,即向OS申请了多少内存使用与used_memory的区别在后面解释。
4、used_memory_peak:
redis内存使用的峰值。
5、used_memory_peak_human:
用户cache数据的峰值大小。
6、used_memory_lua:
执行lua脚本所占用的内存。
7、mem_fragmentation_ratio:
内存碎片率,计算公式:
ratio指数>1表明有内存碎片,越大表明越多,<1表明正在使用虚拟内存,虚拟内存其实就是硬盘,性能比内存低得多,这是应该增强机器的内存以提高性能。一般来说,mem_fragmentation_ratio的数值在1 ~ 1.5之间是比较健康的。
8、mem_allocator:
在编译时指定的Redis使用的内存分配器,可以是libc、jemalloc、tcmalloc,默认是jemalloc。jemalloc在64位系统中,将内存空间划分为小、大、巨大三个范围;每个范围内又划分了许多小的内存块单位;存储数据的时候,会选择大小最合适的内存块进行存储。
jemalloc划分的内存单元如下图所示:
----------------------------------------------------- 分割线 ------------------------------------------------
产生原因
可以这样认为,redis产生内存碎片有两个原因,
A:redis自身的内存分配器。
B:修改cache的值,且修改后的value与原来value的大小差异较大。
进程需要用内存的话,会先通过OS向device申请,然后才能够使用。一般进程在不需要使用的时候,会释放掉这部分内存并返回给device。但是redis作者可能为了更高的性能,所以在redis中实现了自己的内存分配器来管理内存,不会马上返还内存,不用每次都向OS申请了,从而实现高性能。
但是,在内存分配器的那张图片我们知道,redis的每个k-v对初始化的内存大小是最适合的,当这个value改变的并且原来内存大小不适用的时候,就需要重新分配内存了。(但是value存比原来小不知道会不会产生碎片)。重新分配之后,就会有一部分内存redis无法正常回收,一直占用着。
----------------------------------------------------- 分割线 ------------------------------------------------
知道了原因就可以解决问题了,网上找到了两个解决方案:
1、重启redis服务,简单粗暴。
2、redis4.0以上可以使用新增指令来手动回收内存碎片,配置监控使用性能更佳,具体大家可以自己去查。
这里提个问题,如果是单机redis,想要不停服务重启redis,大家有什么好的想法?