fork()失败根本原因是Linux内核vm.overcommit_memory=2模式下严格校验total_vm超限,而非Redis内存耗尽;应设为1并同步调低swappiness至1。

fork()失败根本不是Redis内存用超了
Redis日志里出现 Can't save in background: fork: Cannot allocate memory,不代表Redis进程本身占满了内存——主进程used_memory_rss可能才1.4G,但fork()还是失败。真正卡住的是Linux内核对“未来可能需要的内存”的预判。Redis执行bgsave时,fork()要复制页表并预留写时复制(COW)空间,而内核在vm.overcommit_memory = 2(Debian/Ubuntu默认)下会严格检查:可用物理内存 + swap ≥ 进程 total_vm。Redis主进程的total_vm常达RSS的2~3倍(比如RSS 1.4G,total_vm却显示6G),内核一看“你虚报这么多地址空间,我哪敢答应”,直接返回ENOMEM。
检查系统是否真在用overcommit=2模式
别猜,直接查:
Redis 缓存和数据结构管理技能。通过自然语言操作 Redis,支持 String、Hash、List、Set、ZSet、Stream 等数据结构操作。当用户提到 Redis、缓存、消息队列、会话存储时使用此技能。
- 运行
cat /proc/sys/vm/overcommit_memory:输出2就是罪魁祸首 - 顺手看下
cat /proc/sys/vm/swappiness:如果是0,即使开了swap,内核也不换出匿名页,overcommit_memory=2下更容易判定“不可分配” - 再确认
cat /proc/<pid>/status | grep VmSize</pid>(是redis-server进程号):对比 VmSize(即total_vm)和VmRSS,差距大就坐实是虚拟内存校验问题
临时修复和永久生效的正确操作
改参数不是“试试看”,而是必须同步处理两个点:
- 立即生效:
sudo sysctl vm.overcommit_memory=1(允许内核无条件通过fork) - 同时调低swap倾向:
sudo sysctl vm.swappiness=1(避免swap干扰COW,又保留最低兜底能力) - 永久写入:
echo "vm.overcommit_memory = 1" >> /etc/sysctl.conf和echo "vm.swappiness = 1" >> /etc/sysctl.conf,然后sysctl -p - 切勿只改
overcommit_memory却不调swappiness:在overcommit_memory=2残留环境下,swappiness=0会让问题更顽固
别被“系统还有空闲内存”骗了
free -h 显示还有2G空闲?没用。Linux内存管理看的是total_vm是否可承诺,不是当前RSS是否够用。尤其在容器或VM中,cgroup限制、宿主机overcommit策略、甚至Azure VM的内存弹性机制都可能让fork()在“明明有内存”的情况下失败。最可靠的判断依据永远是:/proc/<pid>/status</pid>里的VmSize值,以及dmesg | grep -i "fork\|oom"有没有相关内核拒绝记录。复杂点在于,这个限制不暴露给Redis自身——它只看到fork返回失败,所有排查必须下沉到系统层。

















