Redis fork失败报“Resource temporarily unavailable”主因是进程数限制触顶,而非内存不足;具体包括ulimit -u、pid_max或cgroup TasksMax耗尽,常见于连接泄漏、容器默认限制过严或systemd服务未配置LimitNPROC。

Redis fork失败时为什么报“Resource temporarily unavailable”
这不是内存不够,而是系统进程数限制被触达了。Redis在执行bgsave或bgrewriteaof时必须调用fork(),而每次fork()都会创建一个新进程——哪怕只存活几秒。当ulimit -u(用户最大进程数)或内核pid_max接近上限,fork()就会直接返回EAGAIN,日志里就显示Resource temporarily unavailable。
常见诱因包括:
- 应用侧未正确关闭Redis连接,大量
redis-cli或客户端连接长期空闲挂起,占用进程+文件描述符 - Redis配置了过小的
timeout但客户端不遵守,导致连接半开堆积 - 容器环境未显式设置
pid_limit,cgroup v2默认限制极低(如1024) - 同一用户下还运行了其他服务(如Node.js集群、Python多进程),共享
ulimit -u
怎么快速确认是句柄/进程数打满而非内存问题
别急着调vm.overcommit_memory,先做三件事:
- 查当前用户进程总数:
ps -u redis | wc -l(假设Redis以redis用户运行) - 对比限制值:
cat /proc/sys/kernel/pid_max和ulimit -u,如果前者接近后者,基本锁定 - 看实时进程增长:
watch -n 1 'ps -u redis | wc -l',触发一次bgsave后观察是否+1且不回收 - 检查cgroup限制(尤其K8s/容器):
cat /sys/fs/cgroup/pids/redis*/pids.current或systemctl show redis | grep TasksMax
如果free -h显示内存充裕,dmesg -T没OOM日志,但fork()仍失败,90%是这个路径。
修复时最容易踩的坑:只改ulimit却不持久化
ulimit -u 65535临时生效,但Redis通过systemd启动时,这个设置根本不会继承过去。真正要改的是systemd服务单元:
- 编辑
/etc/systemd/system/redis.service,在[Service]段加:LimitNPROC=65535 - 如果用了cgroup v2,还需加:
TasksMax=65535 - 重载并重启:
systemctl daemon-reload && systemctl restart redis - 验证是否生效:
systemctl show redis | grep -E "(TasksMax|LimitNPROC)"
注意:LimitNPROC控制的是该service能创建的**线程+进程总数**,不是仅指子进程;设太小会卡住AOF重写,设太大可能掩盖连接泄漏问题。
为什么改完还是偶尔失败?盯紧客户端连接生命周期
进程数持续爬升往往不是Redis自身问题,而是上游没管好连接。关键检查点:
- 客户端是否复用连接池?Java应用常见错误是每次请求都new
Jedis,用完不close() - Redis配置
maxclients是否远高于实际需要?设为10000但业务只用200,等于给泄漏留了巨大空间 - 有没有监控
connected_clients和rejected_connections指标?突增说明连接池配置异常或网络抖动引发重连风暴 -
client list输出中idle字段超长(比如>3600)的连接,大概率是泄漏源
真正稳定的系统,bgsave子进程应是瞬时存在的——fork出来、写完RDB、exit,整个过程在秒级完成。如果子进程长时间存活或数量滞涨,说明父进程或子进程本身已被信号中断、挂起,或者卡在IO等待上,这时得结合strace -p $(pgrep redis)进一步定位。











