最近查看了一下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,大家有什么好的想法?