如何通过Nginx worker_cpu_affinity与numactl协同优化,实现跨核内存访问延迟的最小化策略?
- 内容介绍
- 文章标签
- 相关推荐
本文共计811个文字,预计阅读时间需要4分钟。
该命令将worker进程绑定到特定CPU核心,以优化性能。在双路服务器上,使用以下命令将前4个worker绑定到Node 0的物理核心:
常见错误现象:
- 压测 QPS 上不去,
perf record -e mem-loads,mem-stores显示大量远程内存访问事件 -
cat /proc/<pid>/status | grep Mems_allowed返回00000003(即两个节点都允许),说明未限制内存域 - 即使
worker_cpu_affinity配置正确,taskset -cp <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>看Numa_Hit是否占绝对主导(>95%),Numa_Miss接近 0 - 查 worker 进程的
/proc/<pid>/status中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 策略。
本文共计811个文字,预计阅读时间需要4分钟。
该命令将worker进程绑定到特定CPU核心,以优化性能。在双路服务器上,使用以下命令将前4个worker绑定到Node 0的物理核心:
常见错误现象:
- 压测 QPS 上不去,
perf record -e mem-loads,mem-stores显示大量远程内存访问事件 -
cat /proc/<pid>/status | grep Mems_allowed返回00000003(即两个节点都允许),说明未限制内存域 - 即使
worker_cpu_affinity配置正确,taskset -cp <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>看Numa_Hit是否占绝对主导(>95%),Numa_Miss接近 0 - 查 worker 进程的
/proc/<pid>/status中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 策略。

