得物面试题:深入了解Redis内存碎片及其清理方法,提升Redis性能的重要性与解决策略
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