Redis内存碎片问题是一个在后端开发中经常被问到的面试题,尽管它不是特别常见,但却是Redis性能优化的重要组成部分。这个问题曾在如得物、美团、阿里、字节、携程等公司的面试中被提及,因此无论是否备战面试,了解和掌握内存碎片的概念都对日常开发有很大帮助。

图片

内存碎片的定义

内存碎片可以简单地理解为一些无法被再利用的闲置内存空间。假设操作系统为某个任务分配了32字节的连续内存,但实际存储的数据只需要24字节,那么剩余的8字节如果没有办法被后续的任务所利用,就被称为内存碎片。

虽然Redis的内存碎片不会直接影响其性能,但却会导致内存消耗的增加。

Redis内存碎片产生的原因

Redis内存碎片主要可以归结为两个常见原因:

1. Redis在存储数据时请求的内存空间常常大于实际需要的空间。

Redis的内存分配机制是通过zmalloc方法实现的,该方法除了分配所需的内存大小外,还会额外分配一定的内存(PREFIX_SIZE)。Redis官方对此的描述为:

To store user keys, Redis allocates at most as much memory as the maxmemory setting enables (however there are small extra allocations possible).

以下是zmalloc方法的实现示例:

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字节等)来分配内存。例如,若一个程序请求17字节的内存,jemalloc可能会分配32字节,造成15字节的浪费。不过,jemalloc对内存碎片问题进行了优化,因此一般不会出现严重的碎片化现象。

图片

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

当Redis中的某个数据被删除时,Redis通常不会立即将内存释放回操作系统,这也是造成内存碎片的原因之一。相关信息可以在Redis官方文档中找到。

文档地址:Redis Memory Optimization

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

可以通过执行info memory命令来查看Redis的内存信息。Redis官方文档详细介绍了每个参数的含义,可以参考:Redis INFO命令

图片

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

这意味着,mem_fragmentation_ratio的值越大,表示内存碎片的严重程度越高。

不应简单将used_memory_rss减去used_memory视为内存碎片的大小,因为这个值还包含其他进程的开销以及共享库、堆栈等的开销。

通常情况下,当mem_fragmentation_ratio > 1.5时,建议进行内存碎片的清理。这表示如果使用Redis存储2G的数据,实际上可能需要超过3G的内存。

如果希望快速查看内存碎片率,可以使用以下命令:

> redis-cli -p 6379 info | grep mem_fragmentation_ratio  

内存碎片率也可能小于1的情况,虽然在日常使用中较为罕见,感兴趣的读者可以参考这篇文章:故障分析 | Redis内存碎片率太低该怎么办?

如何清理Redis内存碎片?

从Redis 4.0-RC3版本开始,Redis自带内存整理功能,能够有效预防内存碎片率过高的问题。用户可以通过以下命令启用此功能:

config set activedefrag yes  

清理的具体时机可以通过以下两个参数进行控制:

# 内存碎片占用空间达到 500MB 时开始清理  
config set active-defrag-ignore-bytes 500mb  
# 内存碎片率大于 1.5 时开始清理  
config set active-defrag-threshold-lower 50  

为了减少内存碎片清理对Redis性能的影响,可以通过以下两个参数进行配置:

# 内存碎片清理所占用 CPU 时间的比例不低于 20%  
config set active-defrag-cycle-min 20  
# 内存碎片清理所占用 CPU 时间的比例不高于 50%  
config set active-defrag-cycle-max 50  

此外,重启Redis节点也可以实现内存碎片的整理。如果你使用的是高可用架构的Redis集群,可以将碎片率过高的主节点转为从节点,以便安全重启。

参考资料

[1]jemalloc: https://github.com/jemalloc/jemalloc