Redis内存碎片详解
1. 什么是内存碎片?
你可以将内存碎片简单地理解为那些不可用的空闲内存。
内存碎片(Memory Fragmentation)是指由于频繁的内存分配和释放,导致可用的内存空间变得不连续,从而影响内存利用率和性能。在 Redis 中,内存碎片通常指的是 Redis 向操作系统申请的内存与实际存储数据的内存之间的差值。
内存碎片的计算方式:
碎片率 = Redis 占用的物理内存 Redis 记录的内存使用量 碎片率 = \frac{\text{Redis 占用的物理内存}}{\text{Redis 记录的内存使用量}} 碎片率=Redis 记录的内存使用量Redis 占用的物理内存
其中:
- Redis 占用的物理内存:Redis 进程在系统中实际使用的内存(由
info memory
中的used_memory_rss
统计)。 - Redis 记录的内存使用量:Redis 逻辑上存储数据所占的内存(由
info memory
中的used_memory
统计)。 - 碎片率大于
1.0
表示有内存碎片,碎片率越大,碎片越严重。
2. 为什么会有内存碎片?
Redis 的内存碎片主要来源于以下几个方面:
(1) 内存分配策略
Redis 默认使用 jemalloc 作为内存分配器(可以使用 jemalloc
、glibc malloc
),而 jemalloc
通过分配不同大小的内存块来管理内存。当某些键被删除或修改后,会导致内存块无法被完全回收,进而产生碎片。
(2) 数据的增删改
- 大 key 的删除:当一个大对象(如大型
hash
、list
、set
)被删除时,其占用的内存可能不会立刻释放给操作系统,而是继续留在 Redis 进程中,形成碎片。 - 数据更新:当一个
string
类型的键从100B
扩展到10KB
,Redis 可能会重新分配新的更大的内存空间,旧的内存块可能无法完全复用,从而导致碎片。
(3) 内存回收机制
jemalloc
可能不会将空闲的内存立刻归还给操作系统,而是继续留在 Redis 进程中以备后续使用。- 操作系统本身的内存管理也可能导致碎片,例如
glibc malloc
使用mmap
/brk
分配内存,某些情况下释放的内存不会立刻回收。
3. 如何查看 Redis 的内存碎片?
可以通过 info memory
命令查看 Redis 的内存碎片情况,关注以下几个字段:
127.0.0.1:6379> info memory
输出示例:
used_memory:104857600 # Redis 逻辑上使用的内存(100MB)
used_memory_rss:157286400 # Redis 实际占用的物理内存(150MB)
mem_fragmentation_ratio:1.50 # 内存碎片率(1.5)
关键指标:
mem_fragmentation_ratio
:内存碎片率。- < 1.0:可能 Redis 触发了
swap
,数据被交换到磁盘,影响性能。 - ≈ 1.0:内存利用率较好,没有明显的碎片问题。
- > 1.2:可能有较多的内存碎片,影响性能。
- > 1.5:内存碎片较严重,可能需要手动清理。
- < 1.0:可能 Redis 触发了
4. 如何清理 Redis 的内存碎片?
如果 mem_fragmentation_ratio
过高(通常 >1.3~1.5),可以采取以下措施来减少内存碎片:
(1) 触发 Redis 主动释放内存
可以通过 MEMORY PURGE
(Redis 4.0+)来强制 jemalloc
释放空闲内存:
127.0.0.1:6379> MEMORY PURGE
这会通知 jemalloc
释放未使用的内存给操作系统,可能会减少 used_memory_rss
,但不会影响 used_memory
。
(2) 重启 Redis
如果碎片严重,MEMORY PURGE
无法明显降低 mem_fragmentation_ratio
,可以尝试重启 Redis:
$ systemctl restart redis
注意:重启 Redis 会导致数据丢失(如果没有持久化),在生产环境中应先做好备份。
(3) 重新加载数据
如果 Redis 使用 RDB
或 AOF
持久化,可以尝试 SAVE + FLUSHALL + RELOAD:
127.0.0.1:6379> SAVE # 先保存当前数据到 RDB
127.0.0.1:6379> FLUSHALL # 清空所有数据
127.0.0.1:6379> SYSTEM RELOAD # 重新加载 RDB 文件
这样 Redis 会重新分配内存,降低碎片率。
(4) 调整 jemalloc 参数
如果 jemalloc
产生过多碎片,可以尝试手动调整参数,修改 Redis 启动命令:
MALLOC_CONF=background_thread:true,dirty_decay_time:300,stats_print:true redis-server
dirty_decay_time
影响 jemalloc
的回收策略,适当调整可能减少碎片。
(5) 控制大 key
- 避免存储超大
hash
、list
、set
,可以拆分存储。 - 采用
hash tag
方案,使数据分布在多个 key 上,而不是集中在一个 key 里。
(6) 设置 maxmemory-policy
如果 Redis 用于缓存,可以使用 volatile-lru
或 allkeys-lru
策略,使数据更均衡地被淘汰,减少碎片:
127.0.0.1:6379> CONFIG SET maxmemory-policy allkeys-lru
5. 总结
方法 | 适用场景 | 影响 |
---|---|---|
MEMORY PURGE | 碎片率 1.2~1.5 | 轻量级,减少 RSS |
Redis 重启 | 碎片率 > 1.5 | 影响业务,需备份 |
RDB 重新加载 | 碎片率 > 1.3 | 需启用持久化 |
jemalloc 参数调整 | 长期优化 | 需测试优化效果 |
避免大 key | 结构设计阶段 | 预防性优化 |
maxmemory-policy | 缓存场景 | 影响数据淘汰策略 |
如果 Redis 的 mem_fragmentation_ratio
过高,可以先尝试 MEMORY PURGE
,如果无效则考虑重启 Redis 或优化数据结构。