Redis持久化fork后内存卡顿主因是vm.swappiness=60导致COW页被误换出至Swap,需设为1、禁用THP、overcommit_memory=2并启用numactl--interleave=all协同优化。

Redis 持久化过程(尤其是 RDB 生成或 AOF 重写)触发 fork() 后,子进程与主线程共享页表,一旦发生写时复制(COW),大量内存页被标记为“可换出”,若 vm.swappiness 值过高,内核会立刻将这些冷页塞进 Swap——哪怕物理内存还剩几 GB。这不是内存不足,而是调度策略误伤。
为什么 vm.swappiness=60 会让 Redis 主线程卡在 Swap 上
默认 vm.swappiness=60 意味着:当系统空闲内存低于总内存的 ~40% 时,内核就开始主动把匿名页(如 Redis 堆内存、子进程 COW 页)换出到 Swap。而 Redis fork 后的子进程在 RDB 写盘前会遍历整个内存地址空间,触发大量 COW 页面,这些页面立刻被内核盯上。
- 现象:top 看
%si(swap-in)持续 >1%,iostat -x 1显示so(swap-out)突增,但free -h显示可用内存仍 >2GB - 关键验证:
cat /proc/sys/vm/swappiness输出是否为 60(或更高) - Redis 场景下,这个值毫无意义——它不是为内存敏感服务设计的
- 设为 0 并不等于禁用 Swap,只是让内核只在 OOM 前最后一刻才启用 Swap;设为 1 是更稳妥的生产选择
必须同步关闭透明大页 THP,否则 swappiness 调优无效
vm.swappiness 调低后仍卡顿?大概率是透明大页(THP)在背后捣鬼。启用 THP 时,Redis 内存被组织成 2MB 大页,fork() 时需拷贝整页页表,耗时从毫秒级跳到数百毫秒,且大页更易被内核判定为“冷页”优先换出。
Redis 缓存和数据结构管理技能。通过自然语言操作 Redis,支持 String、Hash、List、Set、ZSet、Stream 等数据结构操作。当用户提到 Redis、缓存、消息队列、会话存储时使用此技能。
- 临时禁用:
echo never > /sys/kernel/mm/transparent_hugepage/enabled - 永久生效:加到
/etc/rc.local或 systemd service 的ExecStartPre中(不能只靠/etc/default/grub加transparent_hugepage=never,某些内核版本不生效) - 验证是否真关掉:
grep -i "mmupage\|mmupf" /proc/$(pgrep redis-server)/smaps | head -5—— 输出中不能出现2048 kB,全是4 kB才算成功 - 别信
cat /sys/kernel/mm/transparent_hugepage/enabled显示[never]就万事大吉,那是当前状态,不是运行时行为
NUMA 架构下,swap 占用高往往和内存分配策略有关
在多路 CPU(如双路 Intel Xeon)+ NUMA 的服务器上,即使 swappiness=1、THP 关闭,Redis 仍可能在 AOF 重写时猛占 Swap。这是因为默认 NUMA 策略下,Redis 主进程被调度到某个 node(比如 node 0),但该 node 的本地内存已满,内核宁愿换出页到 Swap,也不跨 node 分配内存。
- 检查 NUMA 状态:
numactl --hardware,看各 node 的free是否严重不均(注意先echo 1 > /proc/sys/vm/drop_caches清缓存再查) - 强制轮询分配:
numactl --interleave=all redis-server /etc/redis.conf - 不要用
--membind或--cpunodebind,那会加剧局部内存紧张 - 该参数必须加在启动命令最前面,systemd 里要改
ExecStart,不能只改CPUAffinity
overcommit_memory=2 是 fork 安全底线,不是可选项
Redis 持久化依赖 fork(),而 fork() 在 overcommit_memory=0(默认)下,需预估子进程最大虚拟内存用量。若 Redis 占用 12GB 物理内存,fork() 可能因无法保证 12GB 预留空间而直接失败,报错 Can't save in background: fork: Cannot allocate memory——此时系统可能转而更激进地使用 Swap 来腾挪空间。
- 必须设为 2:
echo 2 > /proc/sys/vm/overcommit_memory - 含义:分配内存上限 =
swap + RAM * overcommit_ratio(默认overcommit_ratio=50),对纯内存 Redis 来说,这等价于“只要物理内存够,就允许 fork” - 设为 2 后,
swappiness的作用才真正回归本意:仅调控冷页换出倾向,而非补救 fork 失败的兜底机制 - 该配置重启失效,需写入
/etc/sysctl.conf并执行sysctl -p
真正起效的组合从来不是单点调参:swappiness=1 + overcommit_memory=2 + THP=never + numactl --interleave=all 必须同时落地,缺一不可。最容易漏的是 THP 验证和 NUMA 策略——它们不会报错,只会让前面所有调优看起来“没用”。

















