「网站一上量就 502」「后台点一下转三秒」「大促当天数据库把 CPU 吃满」——这些问题八成不是配置不够,而是默认参数没调。一台 4 核 4G 的机器,用默认的 Nginx + PHP-FPM + MySQL 配置,撑两三千 PV/天就开始喘;同样的机器把参数调对,稳定跑两三万 PV/天是正常的。
这篇是一份可以直接照着改的调优清单,按「系统层 → Nginx → PHP-FPM → MySQL → 缓存与静态化 → 观测」六层。每条都给判断依据和参考值,具体数值要按你的机器规格调整,不要照抄。改配置前先备份,逐项改、逐项压测——一次改十个参数,出了问题你分不清是哪个。
第一步:先量一遍基线,别凭感觉调
没有基线就没有优化。上线前记录这五个数字,后面每次改动都对比:
ab -n 2000 -c 50 https://你的站点/或wrk -t4 -c50 -d30s https://你的站点/:看 QPS 与 P99 延迟vmstat 1:看r(运行队列)与wa(IO 等待)ss -s:看 TIME_WAIT 与 ESTAB 数量top -Hp $(pgrep -f php-fpm | head -1):看 PHP-FPM 子进程 CPU 分布mysqladmin ext -i1 | grep -E "Threads_|Questions|Innodb_buffer":看数据库压力
合格线参考:轻量站的 P99 应低于 500ms,动态接口低于 800ms;r 值持续大于 CPU 核数说明排队;wa 超过 10% 说明磁盘是瓶颈,优先级要先解决 IO 再谈调参。
系统层:五个必须改的默认项
Linux 默认参数是为通用场景设计的,做 Web 服务要改五处:
- 文件句柄数:
ulimit -n默认 1024,几千并发就不够。/etc/security/limits.conf里设nofile 65535,同时改/etc/systemd/system.conf的DefaultLimitNOFILE,否则 systemd 启动的服务不生效。 - TCP 连接队列:
net.core.somaxconn=32768、net.ipv4.tcp_max_syn_backlog=8192,配合 Nginx 的backlog。 - TIME_WAIT 复用:
net.ipv4.tcp_tw_reuse=1、net.ipv4.tcp_fin_timeout=15。注意不要开tcp_tw_recycle,新版内核已移除,开了反而出问题。 - 拥塞控制:
net.ipv4.tcp_congestion_control=bbr,跨境链路上比 cubic 提升明显,尤其有丢包时。 - 文件系统挂载:数据盘加
noatime,减少无意义的元数据写入。
改完 sysctl -p,再 sysctl net.ipv4.tcp_congestion_control 确认生效。
Nginx:让静态和动态各走各的路
Nginx 是入口,它省下来的每一个连接都是 PHP 的救命稻草。四处最值得改:
- worker 与连接数:
worker_processes auto;、worker_connections 10240;、worker_rlimit_nofile 65535;。理论并发上限 = worker 数 × worker_connections,实际按一半估。 - keepalive:
keepalive_timeout 30;、keepalive_requests 1000;。太短会频繁握手,太长会占连接。 - gzip:
gzip on; gzip_min_length 1k; gzip_comp_level 5; gzip_types text/css application/javascript application/json image/svg+xml;。别再压图片,纯浪费 CPU。 - 静态缓存:带指纹的文件(
app.a1b2c3.js)用expires 1y+immutable;不带指纹的用expires 7d。
三件事常被忽略但影响很大:
client_body_buffer_size和client_max_body_size影响上传,后台上传大图报 413 就是这个。fastcgi_buffers太小会导致大页面反复写临时文件,看到an upstream response is buffered to a temporary file就调大。- 开
access_log的 buffer,或对静态资源关日志。高并发下写日志的 IO 很可观。
PHP-FPM:pm 参数决定能扛多少并发
PHP-FPM 的进程模型是「一个请求一个进程」,所以进程数就是并发上限,内存是硬约束。三种 pm 模式:
| 模式 | 适用 | 参数建议 |
|---|---|---|
| static | 流量稳定、内存充足 | pm.max_children = 内存 / 单进程内存(约 40–80MB) |
| dynamic | 流量有峰谷 | max_children 同上,start_servers 取 1/4,min_spare 取 1/8,max_spare 取 1/2 |
| ondemand | 低频访问、内存紧张 | idle_timeout 10s,代价是冷启动慢 |
算法很直接:4G 内存的机器,单进程实测 60MB,留 1G 给系统和数据库,剩下的 3G 能开 50 个进程左右。max_children 设成 200 是常见事故——超出内存就会 OOM,然后整台机器一起挂,比 502 更严重。
另外三个参数值得调:
request_terminate_timeout = 60:防止某个慢请求把所有进程占死。pm.max_requests = 500:进程处理若干请求后重启,规避内存泄漏。slowlog:一定要开,request_slowlog_timeout = 3,慢请求的调用栈会写进日志,排查性能问题全靠它。
还有一项容易被忽略的:OPcache。生产环境必须开,命中率能到 99%,PHP 执行时间直接砍掉一半。
opcache.enable=1、opcache.memory_consumption=256、opcache.max_accelerated_files=20000opcache.validate_timestamps=0:改代码要重启 FPM 才生效,但省掉每次请求的文件检查,收益明显。opcache.interned_strings_buffer=16:字符串多的项目能再省一截内存。
MySQL:三个参数管住 80% 的问题
数据库调优听起来复杂,实际上中小站点把三件事做好就够:
- 缓冲池:
innodb_buffer_pool_size设为物理内存的 50%–70%。这是最重要的一项,数据量小于缓冲池时,读操作基本不落盘。 - 日志提交:
innodb_flush_log_at_trx_commit=1最安全;如果业务能容忍极端情况下丢 1 秒数据,用2能显著提升写入性能。金融类业务不要动这个。 - 连接数与超时:
max_connections按「并发进程数 + 余量」设,别设 1000 空占内存;wait_timeout降到 60–120,避免大量空闲连接。
判断是否要加索引,看慢查询日志,别看感觉:
- 开
slow_query_log=1、long_query_time=0.5 - 用
mysqldumpslow -s t -t 20 slow.log找最耗时的 20 条 - 对每条做
EXPLAIN,重点看type(出现 ALL 是全表扫)、rows(扫描行数)、Extra(Using filesort / Using temporary 都要处理)
经验规则:单表超过 500 万行、或某个查询扫描行数超过总行数 10%,就该考虑索引或分表了。索引不是越多越好,每个索引都会拖慢写入。
能静态就静态,能缓存就缓存
调参的天花板是硬件,缓存的天花板是架构。三层缓存按性价比排:
- 页面缓存:WordPress 用插件把页面存成静态 HTML,Nginx 直接返回,动态请求数能降 90%。这是性价比最高的一步。
- 对象缓存:Redis 存数据库查询结果与会话,命中率 80% 以上时数据库压力接近减半。
- CDN 缓存:图片、CSS、JS 全量走 CDN,回源只保留 HTML 与接口。带宽成本也会降。
要注意缓存失效策略:更新文章后要主动清对应缓存,否则用户看到旧内容。全站清缓存是最省事但最伤性能的做法,尽量按 URL 或按分类清。
观测:出问题时能 30 秒定位
调优做完如果没有监控,等于没做。最少要有四个视图:
- 机器:CPU、内存、磁盘 IO、网络,1 分钟粒度,保留 30 天。
- Nginx:QPS、5xx 比例、上游响应时间(P50/P95/P99)。5xx 突增是最早的预警。
- PHP:进程池使用率(active/max_children)、慢请求数。使用率长期 80% 以上说明该扩容了。
- 数据库:连接数、慢查询数、缓冲池命中率(低于 95% 要加内存)。
再加一条告警规则就够用:5xx 比例连续 3 分钟超过 1%,或者 P95 响应时间超过基线 2 倍,直接推送到手机。别做几十条告警,最后没人看。
常见问题
Q:调完参数反而更慢了,为什么?
最常见的是 max_children 超过内存承受范围,系统开始用 swap。free -h 看 swap 有没有被用,vmstat 1 看 si/so 是否非零。有 swap 交换就说明内存超了,把进程数降回去。
Q:OPcache 开了但没感觉?
看 opcache_get_status() 的命中率。如果命中率低,通常是 max_accelerated_files 太小导致缓存被反复清空,调到 20000 以上再看。
Q:要不要上 Redis?
看数据库是不是瓶颈。如果 Threads_running 经常超过 CPU 核数、慢查询多,上 Redis 收益明显;如果瓶颈在 PHP 进程数不够,上 Redis 帮助有限,先加机器或减进程开销。
Q:4 核 4G 到底能扛多少 PV?
纯动态页面、无缓存,大约 5000–10000 PV/天;开启页面缓存后能到 5 万 PV/天以上;再加 CDN 与对象缓存,跑到十万量级也不奇怪。差别全在架构,不在硬件。
Q:什么时候该加机器而不是继续调参?
两个信号:一是所有参数都到了合理区间,QPS 仍上不去;二是单机故障会导致全站不可用。前者是容量问题,后者是可用性问题,都该往上加一层。
小结
调优的优先级排下来:先量基线 → 改系统参数(句柄、连接队列、BBR)→ Nginx 静态与压缩 → PHP-FPM 进程数与 OPcache → MySQL 缓冲池与慢查询 → 上缓存 → 补监控。前四步都是半小时内能做完的事,收益却最大。
最后提醒一句:所有数值都是起点不是终点。同样的 4 核 4G,跑 WordPress 和跑自研框架的最优参数不一样。改一项、压一次、记一次,三轮之后你会得到一份只属于你这套业务的参数表,那比任何模板都值钱。
相关阅读:买服务器前必须测的 5 个指标、外贸独立站用什么服务器:配置清单、服务器性能自测脚本合集、中小企业上云第一步:三种形态怎么选。
本站深度稿系列:网站速度优化:从3秒到1秒的12个动作、Linux磁盘与IO排查:四类故障的定位路径、Docker上生产:从镜像到升级的16项设置。