这篇有一个遗憾要先说:Active Defrag(主动碎片整理)只在特制的 jemalloc 构建上可用,我这台机器上的 Redis 用的是 libc,开启会直接失败——所以我没法演示「整理」本身。能演示、并且值得演示的是它要解决的问题:碎片是怎么产生的,有多严重。这部分是完整实测的。
mem_allocator = libc
INFO memory 里的 mem_allocator 是 libc。Redis 官方源码自带并默认使用的是定制的 jemalloc;Homebrew 这个构建用了系统的 libc malloc。
> CONFIG SET activedefrag yes (error) ERR CONFIG SET failed (possibly related to argument 'activedefrag') - Active defragmentation cannot be enabled: it requires a Redis server compiled with a modified Jemalloc like the one shipped by default with the Redis source distribution > MEMORY PURGE "OK"
报错原文:Active defragmentation cannot be enabled: it requires a Redis server compiled with a modified Jemalloc like the one shipped by default with the Redis source distribution。所以「主动碎片整理有多有效」这个问题,本文没有答案;需要在 jemalloc 构建(比如官方 Docker 镜像)上测。我这台机器上 Docker 没有运行,没法补上。
写入 100 万个 key 后 used_memory= 171.3 MB used_memory_rss= 180.2 MB mem_fragmentation_ratio=1.10 allocator_frag_ratio=1.00 随机删除 90% 之后 used_memory= 18.2 MB used_memory_rss= 188.8 MB mem_fragmentation_ratio=10.38 allocator_frag_ratio=1.00 used_memory 从 171 MB 降到 18 MB(-89%);used_memory_rss 从 180 MB 到 189 MB(--5%)
写入完成时 used_memory 171 MB、used_memory_rss 180 MB,碎片率 1.10。随机删掉 90% 的 key 之后 used_memory(Redis 自己统计的已分配)降到 18 MB,但 used_memory_rss 仍是 189 MB——碎片率 10.38。原因:被删除的 key 散落在整个堆里,每个大块里都还夹着仍然存活的 key,分配器无法把整块还给系统。(这里是 libc 分配器 + macOS;jemalloc 上的表现会不同,我没有测。)
Sam, I detected a few issues in this Redis instance memory implants: * Peak memory: In the past this instance used more than 150% the memory that is currently using. The allocator is normally not able to release memory after a peak, so you can expect to see a big fragmentation ratio, however this
MEMORY PURGE 让分配器尝试归还内存,在 libc 上返回 OK 但没有可见效果。MEMORY DOCTOR 给出的诊断对应「峰值之后分配器通常无法释放内存」——它没有说这是「碎片」,而是「峰值」;两者在这个实验里其实是同一件事的两种说法。
mem_fragmentation_ratio = RSS ÷ used_memory。远大于 1 表示「进程占着系统的内存,但 Redis 自己已经没在用」;小于 1 则通常意味着发生了 swap。它不等于「可以回收」:回收要靠 active defrag(需要 jemalloc)、重启,或者让新数据重新利用这些空洞。本文只验证了「这个比值会变得很高」,没有验证「怎么降下来」。