Nginx 调优别只盯着 worker 数,先看清瓶颈在哪一层
上周有个做图片站的朋友问我,为什么他的 Nginx 在 4 核 8G 的机器上 QPS 死活上不了 1000。我让他先别动配置,把 ss -s 和 top 各跑一遍。结果 CPU 里 sys 占了 60% 以上,user 才 20% 出头——问题根本不在 Nginx 的 worker 数量,而是系统调用太频繁。
这类情况我见得太多了。很多人一上来就改 worker_processes 和 worker_connections,改完重启发现曲线纹丝不动,然后开始怀疑是不是机器不行。其实 Nginx 调优的顺序应该是:先定位瓶颈在哪个子系统,再动那个子系统对应的参数,最后才考虑加机器。
连接数上不去,先查文件描述符而不是 worker_connections
Nginx 默认的 worker_connections 是 512,worker_processes 是 1。看起来调大这两个数就能扛更多连接,但很多人忽略了它背后的 fd 上限。
Linux 默认单进程 open files 是 1024。你 worker_connections 设成 10240,fd 上限还是 1024,那多出来的连接根本建立不了。改法分两步:
- 在 /etc/security/limits.conf 里给 nginx 用户加 nofile 65535
- 在 nginx.conf 顶部写 worker_rlimit_nofile 65535
这两个改完,重载配置,再用 cat /proc/$(pidof nginx | awk '{print $1}')/limits | grep files 确认生效。我一般会让对方先只改这一处,观察 QPS 有没有跳变——如果有,说明之前就是卡在这;如果没有,再往下查。
需要注意 worker_connections 的数值要跟 fd 上限匹配,设成 65535 的 fd 却只给 10240 的连接数,等于白留余量。
静态文件服务:sendfile 和 tcp_nopush 的组合省的是 CPU
如果站点以图片、JS、CSS 这类静态资源为主,内核态的零拷贝能省下大量 CPU 时间。核心是三个参数:
- sendfile on:数据从磁盘到 socket 不经过用户态
- tcp_nopush on:配合 sendfile,把响应头和文件内容合并成一个包发出去
- tcp_nodelay on:keepalive 连接下禁用 Nagle 算法,降低小包延迟
我自己的经验是,一个纯静态的图片站,开 sendfile 前后 sys CPU 占比能从 55% 掉到 30% 左右。省下来的 CPU 意味着同一台机器能多扛一倍请求,不用急着加内存或者换更高主频的 CPU。
但这里有个取舍:tcp_nopush 和 tcp_nodelay 在某些场景下会互相抵消。如果你的响应体很小、延迟敏感(比如 API 网关),优先保 tcp_nodelay;如果是大文件下载,优先保 tcp_nopush。两个都开不是不行,但要实测确认没有反效果。
反向代理和缓存:proxy_cache 用对了能砍掉一半回源
走到这一层,说明前面的内核和静态优化已经做完了。反代场景下 Nginx 的性能瓶颈通常不在 Nginx 本身,而在它等后端响应的那段时间。
关键参数是 proxy_cache_path 和 proxy_cache_valid。把后端返回的 200 响应缓存到本地磁盘或者 tmpfs,命中后直接由 Nginx 返回,不再走 upstream。一个日 PV 50 万的资讯站,如果缓存命中率做到 70%,回源请求大概能降到原来的三成,后端那台 2 核 4G 的应用服务器就够用了,不用升到 4 核 8G。
缓存路径我建议放在 /dev/shm 下,也就是内存盘。代价是重启丢缓存,但换来的是命中时的读取速度接近内存。如果内容更新频繁、对一致性要求高,那就放 SSD 并缩短 proxy_cache_valid 的时间。
另外 proxy_buffering 默认是开的,后端吐数据慢的时候 Nginx 会先缓冲再转发。如果后端是流式接口,记得关掉它。
最后说一句服务器层面的取舍。调优能把单机压榨到什么程度,跟机器的磁盘 IO 和网络带宽关系很大。如果站点面向亚太用户、又需要多 IP 做站群分发,香港机房的物理服务器延迟优势比较明显,比如 香港自营物理服务器(20M)⑧ 这种 20M 带宽的配置,适合把静态资源和反代缓存放在离用户近的一侧。预算有限、主要跑欧美流量的话,硅谷的 硅谷裸机云 VIII 100M 带宽在回源和分发上更宽松。
回到开头那个朋友的问题:他改完 fd 上限和 sendfile,QPS 从 800 涨到了 2100 左右,CPU 的 sys 占比降到了 25%。机器没换,配置改了四行。所以下次遇到 Nginx 性能问题,先别急着加钱,把这三层按顺序过一遍:fd 和连接数、静态文件的内核参数、反代的缓存策略。多数情况下,瓶颈就藏在你自己没注意的那个默认值里。