fork()失败主因是vm.overcommit_memory=2时内核严格校验内存不足,total_vm远超RSS导致预留失败;应设为1并调低swappiness至1。

Redis RDB子进程 fork 失败或中途退出,通常不是 Redis 自身 bug,而是系统级资源或内核参数限制所致。
为什么 fork() 会失败?
Redis 执行 bgsave 时,主进程会调用 fork() 创建子进程。这个系统调用需要复制父进程的虚拟内存页表(非实际物理内存),但内核仍需预留足够内存空间用于可能的写时复制(COW)。若系统开启 vm.overcommit_memory = 2(默认 Debian/Ubuntu 常见),内核会严格校验:可用内存 + swap 是否 ≥ 当前进程虚拟内存总量。而 Redis 主进程的 total_vm 往往远超其实际 RSS(比如 3GB RSS 却报告 8GB total_vm),导致 fork() 直接返回 ENOMEM,子进程根本无法启动——你看到的日志可能是:
13245:M 06 May 12:45:22.102 # Can't save in background: fork: Cannot allocate memory
此时 Redis 不会崩溃,但 RDB 持久化彻底失效,rdb_bgsave_in_progress 始终为 0,rdb_last_bgsave_status 是 err。
检查 vm.overcommit_memory 和 overcommit_ratio
运行以下命令确认当前策略:
$ cat /proc/sys/vm/overcommit_memory 2 $ cat /proc/sys/vm/overcommit_ratio 50
关键点:
-
overcommit_memory = 0:启发式检查(较宽松,但不保证) -
overcommit_memory = 1:总是允许 fork(最适配 Redis,推荐) -
overcommit_memory = 2:严格模式,依赖overcommit_ratio和swap,极易触发 fork 失败
临时切换(立即生效):
Redis 缓存和数据结构管理技能。通过自然语言操作 Redis,支持 String、Hash、List、Set、ZSet、Stream 等数据结构操作。当用户提到 Redis、缓存、消息队列、会话存储时使用此技能。
sudo sysctl vm.overcommit_memory=1
永久生效需写入 /etc/sysctl.conf:
vm.overcommit_memory = 1
同时必须检查 /proc/sys/vm/swappiness
即使开了 swap,若 swappiness = 0,内核在内存压力下几乎不换出匿名页,会导致 overcommit_memory = 2 下更早判定“不可分配”。Redis 的工作集是匿名内存(堆+页表),对 swap 敏感:
-
swappiness = 0:禁用 swap for anonymous pages →fork()更容易失败 -
swappiness = 1:最低限度启用 → 推荐值,平衡稳定性与性能 - 不要设为 60(默认值):Redis 不适合高频 swap,会拖慢
bgsave且加剧延迟
设置方式:
sudo sysctl vm.swappiness=1
为什么不能只靠 ulimit -v 或 cgroup memory limit?
很多用户尝试用 ulimit -v 限制虚拟内存,或用 cgroup v1 的 memory.limit_in_bytes,但这反而会加剧问题:
-
ulimit -v限制的是单进程虚拟地址空间上限,而fork()需要的是“可承诺内存”,跟该限制无直接关系 - cgroup v1 的
memory.limit_in_bytes在overcommit_memory = 2下会参与计算,但它的粒度太粗,容易误杀 fork 流程 - 真正有效的是调整内核的内存承诺策略本身,而非上层限制
一个常被忽略的细节:/proc/<pid>/status</pid> 中的 VMPeak 和 TotalVm 值,比 redis-cli info memory 的 used_memory 高得多——这才是 fork() 校验的真实依据。

















