redis之内存碎片问题如何解决

内存碎片

什么是内存碎片:当操作系统内存充足,但是却无法申请连续N字节内存空间时,这些剩余的内存空间就叫内存碎片。

内存碎片是如何形成的:内存碎片的形成有两方面的因素,第一个是分配策略,第二个是数据本身的修改,分别来看下。

分配策略

Redis使用的内存分配器,如jemelloc,在进行内存分配时并非需要多少就申请多少,而是按照2的次方来申请的,比如需要的是6字节的内存空间,则会申请8字节的,需要的是13字节则会申请16字节,而这比实际需要的多出的内存空间,就会形成内存碎片。如下图:

其中的红色区域就是内存碎片。

数据本身的修改

我们假定应用A和应用B分别申请了8字节和16自己的内存,然后应用A删除了3字节的数据,则此时就会出现3字节的内存碎片,如下图:

如何判断是否有内存碎片

可以通过info memory命令查看,如下:

127.0.0.1:6379> info memory
# Memory
used_memory:793320
...
used_memory_rss:956392
...
mem_fragmentation_ratio:1.2 
mem_allocator:jemalloc-3.6.0 # 使用的内存分配器(自己加的)

以上used_memory代表实际使用的内存量,used_memory_rss代表实际分配的内存量,mem_fragmentation_ratio是used_memory_rss/used_memory,正常该值应该处于1~1.5之间,如果是大于1.5则说明内存碎片过于严重,需要进行干预处理。

mem_fragmentation_ratio值当出现小于1的情况时,则说明申请的内存不足够使用,而发生了swap,此时因为会有磁盘IO的发生,所以可能会导致响应速度变慢,需要加大内存。

如何清理内存碎片

清理内存碎片最直接的方法就是重启,该方法虽然简单粗暴,但也最简单有效,效果最好,实际工作中如果业务允许也可以使用该方法,但是如果是业务不允许该怎么办呢?在redis4版本中,就提供了碎片自动清理的机制,通过数据移动,空间合并等方法来达到内存碎片清理的目的,这个过程可以参考下图:

需要注意,碎片整理过程中是无法对外提供服务的,因为此时主线程需要拷贝数据,所以对这个时间我们需要进行控制,不能过长。首先想要开启内存碎片整理的话,需要配置 config set activedefrag yes,然后当满足如下条件时开始进行清理:

active-defrag-ignore-bytes 100mb:表示内存碎片的字节数达到 100MB 时,开始清理;
active-defrag-threshold-lower 10:表示内存碎片空间占操作系统分配给 Redis 的总空间比例达到 10% 时,开始清理。

为了控制对Redis性能的影响,提供了如下参数控制清理操作占用的CPU时长:

active-defrag-cycle-min 25: 表示自动清理过程所用 CPU 时间的比例不低于 25%,保证清理能正常开展;
active-defrag-cycle-max 75:表示自动清理过程所用 CPU 时间的比例不高于 75%,一旦超过,就停止清理,从而避免在清理时,大量的内存拷贝阻塞 Redis,导致响应延迟升高。

如果你使用的是Redis4及以上的版本,可以在生产环境将这个配置使用起来。

如果当前内存页的利用率低于平均利用率,即该内存页相对较空闲,代码会倾向于将其中的数据迁移到更加利用率高的内存页中,以期望更好地利用内存空间。为了避免所有内存页利用率相同的情况下的停滞,代码给予额外的12.5%权重来决定是否进行内存碎片整理。目的是在内存碎片整理时,基于内存页的利用率进行决策,以优化内存空间的利用。这是Redis中内存管理和碎片整理的一部分,用于提高内存使用效率和性能。(所以本质就是数据移植,但是只能基于内存页,内部还是操作系统管理的,相关源码说明在jemalloc_internal_inlines_c.h中,大概376行)

Last modification:November 17, 2023
如果觉得我的文章对你有用,请随意赞赏