Worker进程数应设为物理核心数而非逻辑核数,需用lscpu查Core(s) per socket与Socket(s)相乘得出,并按场景调整:长连接减半、I/O密集用满、SSL加解密可略超;同时必须同步配置worker_rlimit_nofile、worker_connections和use epoll。

直接把 worker_processes 降下来,是最有效、最根本的解法。它不是“调优选项”,而是触发锁竞争的开关——值越大,争抢共享内存区(如限流 zone、SSL 缓存、Lua 字典)的 worker 就越多,自旋锁冲突越频繁,延迟抖动越明显。
按物理核心数设上限,别信逻辑核或 auto
先确认真实可用的 CPU 物理核心总数:
执行 lscpu | grep "Core(s) per socket" 和 lscpu | grep "Socket(s)",两者相乘才是真实核心数(例如 2 sockets × 6 cores = 12 核)。
超线程(HT)带来的逻辑核(如显示 24 CPU(s))不能算作并发能力来源,设成 24 会显著抬高上下文切换和锁竞争。
- 容器环境必须看 cgroup 限额:读 cat /sys/fs/cgroup/cpu/cpu.cfs_quota_us,若为 200000(即 2 核配额),worker_processes 只能设 2
- 宝塔面板里显示的“CPU核心数”常混淆逻辑核与物理核,不可直接采信
- auto 在多数物理机上等价于物理核数,但在容器、K8s 或混部场景下容易误判,建议显式写死
启用 reuseport 彻底绕过 accept 锁
Nginx 1.9.1+ 默认开启 accept_mutex on,靠单个 worker 负责 accept,其余等待,虽缓解惊群,但仍是用户态锁。高并发短连接下,这个锁本身就成了瓶颈。
- 在 listen 指令后加 reuseport(需 Linux 3.9+ 且 Nginx 编译时含
--with-stream或启用SO_REUSEPORT支持):listen 80 reuseport; - 启用后,内核自动将新连接分发到不同 socket,每个 worker 独立 accept,accept_mutex 自动失效,不再有 accept 层锁竞争
- 验证方式:
netstat -ltnp | grep :80,若看到多个相同端口对应不同 PID,说明生效
对共享资源做分片(sharding),降低锁粒度
即使 worker 数合理,若所有进程高频更新同一块共享内存(如一个全局计数器、一个 Lua shared_dict),仍会因哈希桶冲突或锁争用导致同步延迟。
- 限流/限连:用多个 zone 分散压力,例如按 URI 前缀或 host 分片:
limit_req_zone $host$uri zone=by_host_uri:10m rate=10r/s; - Lua 场景:避免共用一个
lua_shared_dict存所有指标;按业务维度拆成多个 dict,如req_count_v1、req_count_v2 - SSL 会话缓存:增大
ssl_session_cache大小并确保 key 哈希足够分散,避免 slot 过热
配合调优三项关键参数,防止连锁失效
只改 worker_processes 不够,以下三项不匹配,轻则启动失败,重则锁竞争加剧:
-
worker_rlimit_nofile:必须等于系统对 nginx 用户(如 www)设置的
ulimit -n值(推荐 65535) -
worker_connections:按公式算:
ulimit -n ÷ worker_processes × 0.9,留余量防 FD 耗尽 - use epoll;:Linux 下必须显式声明,否则可能 fallback 到低效事件模型,放大锁等待时间










