worker_cpu_affinity单独用于numa服务器会失效,因它仅绑定cpu不控制内存分配;必须配合numactl --cpunodebind=0 --membind=0启动master进程,使worker继承本地内存策略,并按numa节点均分worker_processes数量。

worker_cpu_affinity 单独用在 NUMA 服务器上会失效
它只管把 worker 进程绑到 CPU 核心,完全不控制内存分配策略。在双路服务器上,哪怕你用 worker_cpu_affinity 0001 0010 0100 1000 把前 4 个 worker 绑定在 Node 0 的物理核上,只要主进程没强制本地内存策略,这些 worker 仍可能从 Node 1 分配内存页——导致跨节点访问延迟翻倍、numastat -p <pid></pid> 显示 numa_miss 持续升高。
常见错误现象:
- 压测 QPS 上不去,
perf record -e mem-loads,mem-stores显示大量远程内存访问事件 -
cat /proc/<pid>/status | grep Mems_allowed</pid>返回00000003(即两个节点都允许),说明未限制内存域 - 即使
worker_cpu_affinity配置正确,taskset -cp <pid></pid>查看绑定生效,性能仍无改善
必须用 numactl 启动 Nginx 主进程才能控制内存亲和
Nginx 自身不支持 numa_bind 或 numa_interleave 等内存策略,所有子进程(包括 worker)继承主进程的 NUMA 策略。所以关键动作是:用 numactl --cpunodebind=0 --membind=0 启动 master 进程,让其 fork 出来的所有 worker 都只使用 Node 0 的 CPU 和内存。
实操建议:
- 双路服务器上,不要写死一个
numactl命令启动全部;应分两组启动:一组绑 Node 0,另一组绑 Node 1(需配合worker_processes拆分) - systemd 场景下,在
/etc/systemd/system/nginx.service.d/override.conf中重写ExecStart:ExecStart=/usr/bin/numactl --cpunodebind=0 --membind=0 /usr/sbin/nginx -g 'daemon off;'
- 若用
auto模式(Nginx ≥1.19.10),仍需numactl,否则worker_cpu_affinity auto只会在全核范围内调度,无法感知 NUMA 边界
worker_processes 数量必须与 NUMA 节点对齐
设成 auto 或总逻辑核数(如 32)是最大误区。真正有效的是按节点均分:Node 0 有 16 核 + 64GB 内存,就最多配 8–12 个 worker(留出 2–4 核给系统中断、SSD I/O 等);Node 1 同理。总数设为 16,不是 32。
为什么不能拉满?
- 每个 worker 默认打开大量文件描述符,跨节点分配 fd 表会加剧内存碎片
- accept mutex 在多 worker 间竞争,节点内通信比跨节点快 3–5 倍,过多 worker 会放大锁争用
-
worker_rlimit_nofile和events.use(如 epoll)的局部性收益随 worker 密度上升而衰减
验证是否真正在本地内存运行
光看 ps 或 taskset 不够,得查内存实际归属:
- 用
numastat -p <master_pid></master_pid>看Numa_Hit是否占绝对主导(>95%),Numa_Miss接近 0 - 查 worker 进程的
/proc/<pid>/status</pid>中Mems_allowed字段:Node 0 应为00000001,Node 1 应为00000002 - 用
perf stat -e numa-migrations对比优化前后迁移次数,下降 90% 以上才算到位
最容易被忽略的一点:Nginx reload(nginx -s reload)会重启 master 进程,但默认不走 numactl 封装——必须确保 reload 触发的也是带 numactl 的启动命令,否则新 worker 全部掉回默认 NUMA 策略。











