Redis bgsave 失败主因是 Linux OOM Killer 在 fork 后、子进程首次触内存前误杀其子进程,表现为日志报 Failed to fork()、dmesg 显示 killed process redis-server,需通过 dmesg 确认并优化内存使用与 fork 时机。

Redis bgsave 为什么突然失败,日志里只看到 Failed to fork()
这不是 Redis 自身 bug,而是 Linux 内核的 OOM Killer 在父进程(Redis server)内存压力大时,把刚 fork 出来的子进程(bgsave 或 bgrewriteaof 子进程)给杀了——它没等子进程真正开始写 RDB,就在 fork() 返回后、exec() 前这个窗口期被干掉了。
典型现象:redis-cli info persistence 显示 rdb_bgsave_in_progress:1,但几秒后变成 0,且 redis.log 出现 Failed to fork(): Cannot allocate memory 或干脆静默失败;用 dmesg -T | grep -i "killed process" 能查到类似 Killed process 12345 (redis-server) 的记录,注意那个 PID 往往是子进程 PID,不是主进程。
-
fork()成功不代表子进程能活下来:Linux 的fork()在启用vm.overcommit_memory=2时会做内存预估,但 OOM Killer 的实际裁决发生在子进程首次触碰内存页时,此时它还没执行write(),只是被当成“潜在吃内存者”误杀 - Redis 主进程内存越接近系统可用内存上限,风险越高;尤其在使用大量
hash/zset且启用了lazyfree-lazy-eviction yes时,主进程 RSS 高但碎片多,fork()后子进程 COW 页面实际映射量远超预期 - 不要依赖
/proc/sys/vm/overcommit_memory设为1来绕过:这会让fork()总是成功,但可能引发后续malloc()失败或更隐蔽的 OOM,不解决根本问题
怎么确认是不是 OOM Killer 干的
别只看 Redis 日志。OOM Killer 的动作默认不输出到应用日志,必须查内核环缓冲区。
- 立刻运行
dmesg -T | grep -i "killed process",重点看时间是否和bgsave失败时刻吻合,进程名是否含redis-server - 检查
/proc/<redis-pid>/status</redis-pid>中的MMUPageSize和MMUPageCount,若后者异常高(比如 > 100 万),说明主进程已积累大量匿名页,fork 开销极大 - 用
cat /proc/sys/vm/oom_kill_allocating_task查值:如果是0(默认),表示内核会扫描所有进程选“最该死”的那个,不一定是当前申请内存的进程;所以子进程被杀很常见
降低 bgsave 被误杀的实际手段
核心思路不是阻止 OOM Killer,而是减少子进程被盯上的概率:让它更轻、更快、更晚触发内存分配。
- 开启
copy-on-write友好配置:在redis.conf中设repl-diskless-sync no(避免 diskless sync 和 bgsave 竞争 fork)、activerehashing no(减少主进程后台内存抖动) - 控制 RDB 触发节奏:用
save ""关闭自动save,改用定时脚本调用redis-cli bgsave,避开业务高峰;同时确保stop-writes-on-bgsave-error yes,防止静默失败导致数据丢失 - 限制单次 fork 开销:如果实例内存 > 10GB,考虑拆分为多个小实例;或者用
redis-cli --pipe+SCAN+DUMP手动导出(慢但绝对不 fork) - 调整内核参数仅作辅助:设
vm.swappiness=1(减少 swap 倾向),但 不要 关闭 swap(OOM Killer 在无 swap 时更激进);可给 Redis 进程设oom_score_adj = -500(echo -500 > /proc/<pid>/oom_score_adj</pid>),但对子进程无效——子进程继承父进程值,而 fork 后它的 oom_score 是独立计算的
为什么不能靠增大系统内存或关闭 OOM Killer 解决
加内存只是推迟问题,关 OOM Killer 会让系统卡死而不是杀进程。
- OOM Killer 是 Linux 内存管理的最后一道防线;禁用它(如设
vm.panic_on_oom=1)会导致内核 panic,比杀一个子进程严重得多 - 即使内存翻倍,只要 Redis 使用率长期 > 85%,fork 时的 COW 开销仍可能触发 OOM;特别是使用
jemalloc且存在大量小对象时,内存碎片会让实际需要的物理页数远高于INFO memory显示的used_memory_rss - 真正难处理的是“fork 窗口期”:子进程从
fork()返回到第一次write()RDB 文件之间,没有任何 Redis 逻辑可干预,全由内核调度——这也是为什么监控fork()耗时(latency monitor)比监控bgsave时间更有预警价值
关键点在于:OOM Killer 不针对 Redis,它针对的是“当前最占内存又最容易释放”的进程;而 bgsave 子进程恰好满足“容易释放”(没持有文件句柄、没修改数据)又“看起来很占内存”(COW 映射了全部页表)这两个条件。处理它,得从内存使用模式和 fork 时机入手,而不是指望内核手下留情。










