worker_processes auto在numa服务器上易失效,因其仅读取逻辑cpu总数,不感知物理插槽、numa节点及内存本地性,导致worker跨节点调度,引发内存访问延迟飙升2–3倍。

worker_processes auto 在 NUMA 多路服务器上不是“开箱即用”的最优解,必须配合 worker_cpu_affinity 和外部启动控制(如 numactl),否则 Worker 进程可能跨 NUMA 节点调度,导致内存访问延迟飙升 2–3 倍。
为什么 auto 模式在 NUMA 服务器上容易失效
Nginx 的 worker_processes auto 仅读取 /proc/cpuinfo 中的逻辑 CPU 总数,不感知物理插槽(Socket)、NUMA 节点或内存本地性。在双路 32 核(2×16)服务器上,它会默认启 32 个 Worker,但内核调度器可能把第 17 个进程扔到远端 NUMA 节点——而该节点本地没有 Nginx 加载的配置、缓存文件或共享内存段。
此时会出现典型现象:htop 显示 CPU 利用率不高,但 perf stat -e cycles,instructions,mem-loads,mem-stores 显示每秒内存加载延迟(mem-loads 的平均 cycle latency)翻倍,numastat -p $(pgrep nginx | head -1) 显示 numa_hit 低于 60%,numa_miss 持续上升。
解决思路不是减少 Worker 数,而是让每个 Worker 严格绑定在“其内存可直达”的 CPU 核心上:
- 先用
lscpu确认 NUMA 节点分布,例如:Node 0 对应 CPU 0–15,Node 1 对应 CPU 16–31 - 用
numactl --hardware验证各节点的本地内存容量与延迟 - 设置
worker_processes 16(单节点核心数),而非 32 - 在
events块中显式配置worker_cpu_affinity,只使用 Node 0 的核心掩码
worker_cpu_affinity 如何匹配 NUMA 节点编号
worker_cpu_affinity 的二进制掩码顺序必须与 Linux 内核报告的 CPU 编号严格一致,而该编号通常按物理插槽顺序排列。错误配置会导致 Worker 绑定到远端节点核心,适得其反。
以双路 Intel Xeon Platinum 8360Y(每路 24 核,共 48 逻辑 CPU)为例,典型拓扑为:
Node 0: CPU 0–23<br>Node 1: CPU 24–47
若只想让 Nginx 运行在 Node 0,则应:
- 设
worker_processes 24 - 写
worker_cpu_affinity 000000000000000000000001 000000000000000000000010 ...(共 24 项,每项 1 位为 1,其余为 0) - 更安全的写法是用十六进制掩码(Nginx 1.21.4+ 支持):
worker_cpu_affinity 00000001 00000002 00000004 ...
注意:不要用 auto 模式生成掩码,它无法区分 NUMA 边界;也不要跳过核心编号(如只写 CPU 0,2,4…),这会破坏亲和性连续性,影响 epoll 批量就绪事件分发效率。
必须用 numactl 启动才能真正隔离内存
仅靠 worker_cpu_affinity 只能约束 CPU 调度,不能限制内存分配策略。Nginx 主进程 fork 出 Worker 后,Worker 分配的内存页仍可能落在远端 NUMA 节点。
正确做法是用 numactl 封装 Nginx 启动命令,在 systemd service 文件中修改 ExecStart:
ExecStart=/usr/bin/numactl --cpunodebind=0 --membind=0 /usr/sbin/nginx -g 'daemon off;'
关键参数说明:
-
--cpunodebind=0:强制所有线程(包括主进程)只能在 Node 0 的 CPU 上运行 -
--membind=0:强制所有内存分配只从 Node 0 的本地内存池满足 - 若需双节点负载均衡,改用
--interleave=all,但此时worker_cpu_affinity应禁用或设为全 1,避免冲突
验证是否生效:numastat -p $(pgrep nginx | head -1) 中 numa_hit 应 >95%,且 numa_foreign 接近 0。
压测时最容易被忽略的三个 NUMA 相关瓶颈
即使 worker_cpu_affinity 和 numactl 都配对了,以下三点仍常导致性能卡在 70% 左右不上升:
-
open_file_cache默认启用,但缓存条目存储在共享内存区,若未绑定 NUMA 节点,查找时会触发跨节点内存访问 → 改为open_file_cache off或确保其内存分配受numactl约束 -
proxy_buffering on时,缓冲区内存来自堆,不受--membind影响 → 改用proxy_buffering off或调小proxy_buffers数量 - 日志写入(
access_log)若落在远端磁盘(如 NVMe 绑定在 Node 1),会产生隐式跨节点 I/O → 将日志路径挂载到 Node 0 直连的 SSD,或用rsyslog转发至本地 syslogd
NUMA 调优不是配完就跑,而是每次变更后都必须用 numastat + perf + 实际 QPS 对比三者交叉验证。一个核心没绑对,或一块内存没锁住,吞吐量就可能掉 30%。











