Redis fork失败头号原因是vm.overcommit_memory=0,内核启发式检查误判total_vm过大而拒绝预留内存;应设为1并调低swappiness至1,且需在宿主机全局配置生效。

vm.overcommit_memory=0 是 fork 失败的头号原因
Redis 每次 RDB 或 AOF rewrite 都要 fork,而内核在 fork 前会检查内存预留是否“合规”。vm.overcommit_memory = 0(Debian/Ubuntu 默认)启用启发式检查,但 Redis 主进程的 total_vm(虚拟内存大小)常远大于 RSS(实际占用),比如 RSS 4GB,total_vm 却达 12GB。内核误判为“内存不足”,直接拒绝 fork,日志里出现:Can't save in background: fork: Cannot allocate memory 或 WARNING overcommit_memory is set to 0!。
这不是磁盘满、也不是物理内存真耗尽,是策略卡死。必须改:
- 临时生效:
sudo sysctl vm.overcommit_memory=1 - 永久生效:
echo "vm.overcommit_memory = 1" | sudo tee -a /etc/sysctl.conf && sudo sysctl -p - 顺手调低
swappiness:设为1(sudo sysctl vm.swappiness=1),避免匿名页 swap 被禁用导致策略失效
大实例 fork 停顿本身不可免,但可量化与收敛
fork 停顿时间取决于页表复制开销,和 Redis 实例 RSS 成正比。一个 16GB 的实例,fork 可能卡住 5–20ms;若此时恰逢 QPS 上万,大量请求就会排队等待,P99 延迟瞬间上扬。
别靠猜,用 latest_fork_usec 看真实耗时:
- 查最近一次:
redis-cli INFO stats | grep latest_fork_usec - 典型参考值:RSS ≤ 2GB →
latest_fork_usec通常 5ms - 如果该值持续 > 10ms,说明实例已超安全水位,需考虑拆分或降级 RDB 频率
net.core.somaxconn 和 tcp-backlog 不匹配会放大 fork 影响
当主节点正在 fork 子进程做 RDB,同时又有大量从节点发起连接(比如集群扩从、故障恢复),accept() 处理变慢,全连接队列就容易溢出。一旦溢出,新连接被内核静默丢弃,现象是部分从节点始终连不上,redis-cli INFO replication 里看不到自己。
关键不是单点调大,而是两端对齐:
- Linux 层:设
net.core.somaxconn = 65535(写入/etc/sysctl.conf并sysctl -p) - Redis 层:在
redis.conf中配tcp-backlog 65535(改完需重启) - 不一致时,哪怕
fork本身只停 2ms,也会因连接失败引发重试风暴,把抖动扩散成大面积延迟
transparent_hugepage 开启会让 fork 更慢
Linux 默认开启 transparent_hugepage(THP),它把多个 4KB 页合并成 2MB 大页以提升吞吐。但 fork 时,内核需为每个大页单独处理 COW,反而增加页表遍历和复制开销,实测会使 latest_fork_usec 增加 20%–40%。
生产环境应禁用:
- 临时禁用:
echo never | sudo tee /sys/kernel/mm/transparent_hugepage/enabled - 永久禁用:在
/etc/rc.local(或 systemd service 中)加入上述命令,或通过内核启动参数transparent_hugepage=never - 注意:禁用 THP 后,务必确认业务无明显随机读性能下降(一般影响极小,但 OLAP 类场景需验证)
真正难处理的不是单个参数怎么设,而是这些配置之间存在隐性耦合——比如 vm.overcommit_memory=1 能让 fork 不失败,但如果 transparent_hugepage 还开着,fork 就依然很慢;再比如 fork 停顿短了,但 tcp-backlog 没跟上,连接失败又会触发客户端重试,反过来加重主线程负担。调优得一块一块试,每改一项都盯紧 latest_fork_usec 和 redis-cli INFO clients 里的连接数波动。


















