Nginx 单机性能极限调优实战:worker 进程与内核参数配置

为什么单机性能还没吃满,网站却先卡了

不少站长遇到过这种情况:服务器 CPU 占用不高、内存也够用,可一到晚高峰并发上来,页面打开就是慢半拍,甚至直接超时。查了一圈代码没问题、数据库也没慢查询,最后才发现是 Nginx 的 worker 进程模型和 Linux 内核参数根本没按机器的真实配置调过。默认配置是为"别出错"设计的,不是为"跑满性能"设计的,尤其是那些用一键包装的 Nginx,events 块里的连接数、http 块里的传输指令基本都是出厂值。这篇文章从 worker 进程讲到内核参数,把个人服务器上 Nginx 单机性能还能从哪里再榨出油水,一条一条说清楚,每段配置都给出理由,方便你按自己的机器情况取舍。

一、调优之前先看清瓶颈在哪

动手改配置前,先确认瓶颈确实在 Nginx 这一层,不然就是白费力气。最简单的办法是用压测工具做一个基准,先装工具再跑两条命令:

apt install apache2-utils   # 提供 ab 命令
ab -n 2000 -c 50 https://你的域名/
ab -n 2000 -c 200 https://你的域名/

对比两次结果里的 Requests per second 和 Time per request 两项指标。如果并发从 50 提到 200 之后,吞吐量几乎不涨、失败请求变多、平均响应时间成倍拉长,说明连接处理能力到顶了,这才是本文要调的部分。如果压的是动态页面并且瓶颈在 PHP-FPM 或者数据库,那要先解决那边,别急着动 Nginx。压测时用 top 顺带看一眼 CPU 和内存,单核 VPS 与四核 VPS 的调法完全不同,先搞清楚自己手上有几张牌。另外提醒一句,压测要挑业务低谷期做,别拿线上正在高峰的机器测试,ab 的高并发请求本身也是一种负载。

二、worker 进程数:不是越多越好

Nginx 采用 master 加 worker 的进程模型,master 负责读取配置、管理信号,真正干活的是 worker 进程。worker_processes 最常见的错误是照抄网上的"设为 CPU 核数",四核机器写 4 没毛病,但两核机器写 8 反而会因为进程频繁切换变慢。最省心的写法是交给 Nginx 自己判断:

worker_processes auto;

auto 会让 Nginx 按 CPU 核数自动生成等量的 worker。想再极致一点,可以显式指定数量并绑定 CPU 亲和性,让每个 worker 固定跑在一个核心上,减少 CPU 缓存失效和进程迁移的开销:

worker_processes 4;

worker_cpu_affinity 0001 0010 0100 1000;

上面是四核机器四个 worker 各绑一个核的写法,掩码按二进制位对应 CPU 编号,从右往左第一位是一号核。绑核前先确认一下机器是不是独占核心,很多廉价云服务器开了 CPU 超售,/proc/cpuinfo 里看到的核数并不是你独享的,这种情况下直接 auto 反而更好。另外可以给 worker 进程稍微提高一点调度优先级,让它在系统繁忙时优先拿到 CPU:

worker_priority -5;

Linux 的 nice 值默认是 0,数值越小优先级越高,设成 -5 表示比普通进程更优先。最后别忘了还有一个配套参数 worker_rlimit_nofile,它决定单个 worker 能打开的文件描述符上限,默认继承系统的 1024,高并发下必须调大,否则会报 too many open files:

worker_rlimit_nofile 65535;

这个参数和 events 块里的 worker_connections 是配套的,理论上单进程最大连接数不能超过文件描述符上限,两者一起调才有效果。

三、连接数与事件模型

每个 worker 能同时处理的连接数由 worker_connections 控制,默认 1024 对个人站往往不够用。注意这个数字是连接总数,包括了空闲的 keepalive 连接,不是活跃请求数,浏览器和爬虫挂着不动的连接都算在里面。配置写在 events 块里:

events {

worker_connections 4096;
use epoll;
multi_accept on;

}

Linux 下事件模型选 epoll 就对了,这是 Nginx 在 Linux 平台上的默认值,写出来是为了明确意图,也方便以后换到别的平台时知道该改哪里。multi_accept on 让 worker 一次 accept 多个新连接,而不是一个一个处理,对瞬时突发的连接有帮助,代价是单个 worker 单次唤醒的处理时间变长,权衡下来一般还是开着划算。理论上一台机器的最大并发连接数约等于 worker 数乘以 worker_connections,四核配 4096 就是一万六千左右,个人网站远远够用。但要记住每个连接都要占用内存,连接数配得太大而内存只有 1G 的话,连接还没到上限内存先爆了,贪心要不得,小内存机器 2048 就差不多了。

四、网络发送与文件传输指令

http 块里这几个指令对静态文件和小页面影响最直接,也是改动性价比最高的几个:

sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 30;
keepalive_requests 300;
reset_timedout_connection on;

sendfile 让内核直接把文件从磁盘搬运到网卡,跳过用户态拷贝,是静态文件性能的关键,必须开。tcp_nopush 与 sendfile 搭配,让内核攒够一个完整的数据包再发出去,减少网络上小包的数量;tcp_nodelay 则是针对实时性敏感的小响应关闭 Nagle 算法,避免小数据在缓冲区里等太久。两个指令侧重点不同,一个偏向吞吐一个偏向延迟,可以同时开启,Nginx 官方默认就是都开的。keepalive_timeout 控制长连接空闲多久后关闭,个人站设 30 秒比较合适,太长会占着连接不放手,太短则浪费复用机会;keepalive_requests 设大一点,比如 300,让一个连接能多服务几百次请求,减少反复握手的开销,对 HTTPS 站点尤其明显,因为省掉的是一次完整的 TLS 握手。reset_timedout_connection 会在连接超时后直接发 RST 重置,而不是走完四次挥手,能更快释放服务器这边的资源。

静态资源多的站还值得开文件句柄缓存,命中缓存的请求能省掉每次打开文件、读取元数据的系统调用,收益很明显:

open_file_cache max=4096 inactive=60s;
open_file_cache_valid 60s;
open_file_cache_min_uses 2;

max 是缓存上限,inactive 表示文件在 60 秒内没被访问就淘汰,open_file_cache_min_uses 2 表示文件被访问两次以上才进入缓存,避免把一次性的冷文件也缓存进来浪费空间。如果你的站点还开着 gzip,把压缩级别和要压缩的类型也顺手确认一下,文本类资源压缩对带宽小的服务器提升比调任何内核参数都立竿见影:

gzip on;
gzip_comp_level 6;
gzip_min_length 1k;
gzip_types text/plain text/css application/javascript application/json image/svg+xml;

级别 6 是性价比比较高的档位,再往上 CPU 开销涨得快而体积几乎不再缩小。已经压缩过的资源比如图片、视频、zip 包不要列进 gzip_types,白白浪费 CPU。

五、内核参数:Nginx 之外的半张王牌

Nginx 配置调到顶之后,系统层往往还有瓶颈,最常见的就是文件描述符限制。Linux 默认的 ulimit -n 是 1024,一个高并发 Nginx 分分钟用完,表现为日志里刷 too many open files。先临时放开验证效果,再写进配置文件永久生效:

ulimit -n 65535   # 临时生效,重开终端失效

追加到 /etc/security/limits.conf,永久生效

  • soft nofile 65535
  • hard nofile 65535

改完 limits.conf 需要重新登录或者重启才生效,用 ulimit -n 验证是否已变成 65535。接下来调整内核网络参数,编辑 /etc/sysctl.conf 追加以下内容:

fs.file-max = 2097152

net.core.somaxconn = 65535
net.ipv4.tcp_max_syn_backlog = 65535
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_fin_timeout = 15
net.ipv4.tcp_syncookies = 1

fs.file-max 是系统级的文件句柄总上限,limits.conf 管的是单进程,这一层管的是整台机器。net.core.somaxconn 决定 listen 队列能排多长,Nginx 的 backlog 参数默认就取它,队列满了内核会直接丢连接,客户端表现就是"连接被重置";tcp_max_syn_backlog 是半连接队列的长度,遭遇 SYN 洪水时这个值越大越扛得住。tcp_tw_reuse 让处于 TIME_WAIT 状态的连接尽快复用端口,配合调小 tcp_fin_timeout(默认 60 秒),能明显缓解短连接场景下端口枯竭的问题。tcp_syncookies 开启后,半连接队列满时会用 SYN Cookie 机制继续处理新连接,是防 SYN 洪水的兜底手段。改完执行 sysctl -p 立即生效。

这里还有一个最容易漏的环节:如果你的 Nginx 是用 systemd 管理的(现在基本都是),它的进程限制由 systemd 决定而不是 limits.conf。查看服务目录下有没有 override 文件,确认 LimitNOFILE 也调大了:

systemctl cat nginx        # 查看当前生效的完整配置
mkdir -p /etc/systemd/system/nginx.service.d

写入 /etc/systemd/system/nginx.service.d/override.conf

[Service]
LimitNOFILE=65535
systemctl daemon-reload && systemctl restart nginx

改完用 cat /proc/进程号/limits 确认生效。改了 systemd 之后 Nginx 的 worker_rlimit_nofile 才有意义,两层限制是相乘的关系,任何一层没放开都是白搭。

六、改完怎么验证而不是自我感动

配置改完,改了 events 块和进程数必须 restart,reload 对这些参数不生效。重启后跑一遍和第一步完全相同的压测命令,对比三个指标:吞吐量、平均响应时间、失败请求数。正常的提升是吞吐量上涨、失败归零、响应时间下降。如果数字没变化,先怀疑配置根本没生效:nginx -T 会打印出最终生效的完整配置,逐段确认没有写错层级,events 指令误放进 http 块不会报错但也不会生效,这类错误最隐蔽。再用 ss -s 看系统连接状态,用 top 看各个 worker 的 CPU 占用是否均匀分布到了多个核心,如果只有一个核在忙,说明 worker 数量或者 CPU 亲和性没配对。

最后说句实在话:单机调优是有天花板的,调完 Nginx 还要回头看 PHP-FPM 的进程数、MySQL 的连接数,任何一个短板都会拖后腿。个人站流量没起来之前,把精力花在页面缓存和代码质量上,比死磕内核参数划算得多。这套配置真正发挥作用的时候,是你某天文章上了热搜、流量突然翻几十倍的那一刻——到那时你已经提前准备好了,而不是半夜爬起来加配置。

Last modification:September 3rd, 2026 at 07:57 am

Leave a Comment