Redis内存碎片详解

什么是内存碎片?

你可以将内存碎片简单地理解为那些不可用的空闲内存。

举个例子:操作系统为你分配了 32 字节的连续内存空间,而你存储数据实际只需要使用 24 字节内存空间,那这多余出来的 8 字节内存空间如果后续没办法再被分配存储其他数据的话,就可以被称为内存碎片。

Redis 内存碎片虽然不会影响 Redis 性能,但是会增加内存消耗

为什么会有 Redis 内存碎片?

Redis 内存碎片产生比较常见的 2 个原因:

1、Redis 存储数据的时候向操作系统申请的内存空间可能会大于数据实际需要的存储空间。

以下是这段 Redis 官方的原话:

To store user keys, Redis allocates at most as much memory as the maxmemory setting enables (however there are small extra allocations possible).下面是这段话的一个解释:

这句话的意思是,Redis 会根据配置项 maxmemory 来限制用于存储用户键(即数据)的内存量。当你设置了 maxmemory 参数后,Redis 将不会使用超过这个设定值的内存来存储数据。

然而,需要注意的是,除了用于存储键值对的内存之外,Redis 还会进行一些额外的小量内存分配。这些额外的内存分配主要用于:

  • Redis 内部的数据结构和元数据,如哈希表、跳跃列表等,它们用于高效地管理和索引键。
  • 系统开销,包括但不限于客户端连接信息、命令解析、AOF重写或RDB快照时的缓冲区等。
  • 在执行某些操作时,比如命令的执行过程中,可能会有临时对象被创建,这些也会占用额外的内存。

因此,虽然 maxmemory 设置限制了Redis可以用来存储用户数据的最大内存,但实际使用的总内存量可能会略高于这个值,因为包含了上述提到的额外内存开销。当Redis达到 maxmemory 限制时,它将根据配置的淘汰策略(eviction policy)开始移除一些数据以释放空间。

Redis 使用 zmalloc 方法(Redis 自己实现的内存分配方法)进行内存分配的时候,除了要分配 size 大小的内存之外,还会多分配 PREFIX_SIZE 大小的内存。

zmalloc 方法源码如下(源码地址:https://github.com/antirez/redis-tools/blob/master/zmalloc.c):

void *zmalloc(size_t size) {

// 分配指定大小的内存

void *ptr = malloc(size+PREFIX_SIZE);

if (!ptr) zmalloc_oom_handler(size);

#ifdef HAVE_MALLOC_SIZE

update_zmalloc_stat_alloc(zmalloc_size(ptr));

return ptr;

#else

*((size_t*)ptr) = size;

update_zmalloc_stat_alloc(size+PREFIX_SIZE);

return (char*)ptr+PREFIX_SIZE;

#endif

}

另外,Redis 可以使用多种内存分配器来分配内存( libc、jemalloc、tcmalloc),默认使用 jemalloc,而 jemalloc 按照一系列固定的大小(8 字节、16 字节、32 字节......)来分配内存的。jemalloc 划分的内存单元如下图所示:

当程序申请的内存最接近某个固定值时,jemalloc 会给它分配相应大小的空间,就比如说程序需要申请 17 字节的内存,jemalloc 会直接给它分配 32 字节的内存,这样会导致有 15 字节内存的浪费。不过,jemalloc 专门针对内存碎片问题做了优化,一般不会存在过度碎片化的问题。

2、频繁修改 Redis 中的数据也会产生内存碎片。

当 Redis 中的某个数据删除时,Redis 通常不会轻易释放内存给操作系统。

这个在 Redis 官方文档中也有对应的原话:

文档地址:Memory optimization | Docs

如何查看 Redis 内存碎片的信息?

使用 info memory 命令即可查看 Redis 内存相关的信息。下图中每个参数具体的含义,Redis 官方文档有详细的介绍:INFO | Docs

Redis 内存碎片率的计算公式:mem_fragmentation_ratio (内存碎片率)= used_memory_rss (操作系统实际分配给 Redis 的物理内存空间大小)/ used_memory(Redis 内存分配器为了存储数据实际申请使用的内存空间大小)

也就是说,mem_fragmentation_ratio (内存碎片率)的值越大代表内存碎片率越严重。

一定不要误认为used_memory_rss 减去 used_memory值就是内存碎片的大小!!!这不仅包括内存碎片,还包括其他进程开销,以及共享库、堆栈等的开销。

很多小伙伴可能要问了:"多大的内存碎片率才是需要清理呢?"。

通常情况下,我们认为 mem_fragmentation_ratio > 1.5 的话才需要清理内存碎片。 mem_fragmentation_ratio > 1.5 意味着你使用 Redis 存储实际大小 2G 的数据需要使用大于 3G 的内存。

如果想要快速查看内存碎片率的话,你还可以通过下面这个命令:

redis-cli -p 6379 info | grep mem_fragmentation_ratio

如何清理 Redis 内存碎片?

Redis4.0-RC3 版本以后自带了内存整理,可以避免内存碎片率过大的问题。

直接通过 config set 命令将 activedefrag 配置项设置为 yes 即可

config set activedefrag yes

具体什么时候清理需要通过下面两个参数控制:

内存碎片占用空间达到 500mb 的时候开始清理

config set active-defrag-ignore-bytes 500mb

内存碎片率大于 1.5 的时候开始清理

config set active-defrag-threshold-lower 50

通过 Redis 自动内存碎片清理机制可能会对 Redis 的性能产生影响,我们可以通过下面两个参数来减少对 Redis 性能的影响:

内存碎片清理所占用 CPU 时间的比例不低于 20%

config set active-defrag-cycle-min 20

内存碎片清理所占用 CPU 时间的比例不高于 50%

config set active-defrag-cycle-max 50

另外,重启节点可以做到内存碎片重新整理。如果你采用的是高可用架构的 Redis 集群的话,你可以将碎片率过高的主节点转换为从节点,以便进行安全重启。

相关推荐
守护者170几秒前
JAVA学习-练习试用Java实现“使用Arrays.toString方法将数组转换为字符串并打印出来”
java·学习
源码哥_博纳软云2 分钟前
JAVA同城服务场馆门店预约系统支持H5小程序APP源码
java·开发语言·微信小程序·小程序·微信公众平台
禾高网络4 分钟前
租赁小程序成品|租赁系统搭建核心功能
java·人工智能·小程序
学会沉淀。10 分钟前
Docker学习
java·开发语言·学习
如若12311 分钟前
对文件内的文件名生成目录,方便查阅
java·前端·python
西猫雷婶40 分钟前
python学opencv|读取图像(二十一)使用cv2.circle()绘制圆形进阶
开发语言·python·opencv
kiiila40 分钟前
【Qt】对象树(生命周期管理)和字符集(cout打印乱码问题)
开发语言·qt
初晴~41 分钟前
【Redis分布式锁】高并发场景下秒杀业务的实现思路(集群模式)
java·数据库·redis·分布式·后端·spring·
小_太_阳1 小时前
Scala_【2】变量和数据类型
开发语言·后端·scala·intellij-idea
直裾1 小时前
scala借阅图书保存记录(三)
开发语言·后端·scala