结论: 服务器性能优化的见效顺序是:先加缓存,再调参数,最后才加硬件。本文给出一张可以直接照抄的参数清单——Linux 内核侧调 somaxconn=65535、fs.file-max=2000000、vm.swappiness=10、ulimit -n=100000;Web 侧调 Nginx 的 worker_processes auto、worker_connections、keepalive_timeout、gzip、open_file_cache;数据库侧把 innodb_buffer_pool_size 设为内存的 50%~70%;每个参数都附推荐值、影响与修改方式。
优化之前:先建立基线
结论:没有基线就不要优化。 改参数前必须知道现在是多少,改完后必须能量化对比,否则你分不清是优化生效了还是流量正好降了。
优化的正确顺序是:应用层缓存 → 数据库索引与慢查询 → Web 服务器参数 → 操作系统内核参数 → 硬件扩容。前两项的收益通常是后三项的十倍以上,却常被跳过。
先花五分钟采集基线:
# 1. 系统整体:负载、内存、swap
uptime; free -h; vmstat 1 5
# 2. 磁盘 IO:%util 接近 100% 说明磁盘是瓶颈
iostat -x 1 5
# 3. 网络:看 TIME_WAIT、ESTABLISHED 数量与重传率
ss -s; netstat -n | awk '/^tcp/{++S[$NF]} END{for(k in S) print k, S[k]}'
# 4. 文件描述符:当前使用量与上限
cat /proc/sys/fs/file-nr; ulimit -n
# 5. Web 层:QPS 与 P99 耗时
tail -n 10000 /var/log/nginx/access.log | awk '{print $NF}' | sort -n \
| awk '{a[NR]=$1} END{printf "P50=%.3fs P95=%.3fs P99=%.3fs\n", a[int(NR*0.5)], a[int(NR*0.95)], a[int(NR*0.99)]}'把以上输出存成一份文件,优化后重跑一遍对比,这是唯一可信的验收方式。
一、Linux 内核参数优化清单
结论:内核参数只需调十个左右,就能覆盖 90% 的 Web 服务器场景。 下表的推荐值适用于 4 核 8G~16 核 32G 的常规 Web/数据库服务器。
| 参数 | 推荐值 | 影响说明 | 修改方式 |
|---|---|---|---|
net.core.somaxconn | 65535 | 全连接队列上限,默认 128。高并发时队列溢出会导致连接被丢弃,Nginx 日志出现 connection reset | sysctl -w net.core.somaxconn=65535 |
net.core.netdev_max_backlog | 250000 | 网卡接收队列长度,高流量下防止丢包 | sysctl -w net.core.netdev_max_backlog=250000 |
net.ipv4.tcp_max_syn_backlog | 65535 | 半连接(SYN)队列长度,防 SYN Flood 时误伤正常用户 | sysctl -w net.ipv4.tcp_max_syn_backlog=65535 |
net.ipv4.tcp_tw_reuse | 1 | 允许复用 TIME_WAIT 套接字用于出站连接,显著降低 TIME_WAIT 堆积 | sysctl -w net.ipv4.tcp_tw_reuse=1 |
net.ipv4.tcp_fin_timeout | 15 | TIME_WAIT 回收时间从默认 60 秒降到 15 秒 | sysctl -w net.ipv4.tcp_fin_timeout=15 |
net.ipv4.ip_local_port_range | 1024 65535 | 可用本地端口范围,决定单机对外最大连接数 | sysctl -w net.ipv4.ip_local_port_range="1024 65535" |
net.ipv4.tcp_keepalive_time | 600 | TCP 保活探测间隔从 7200 秒降到 600 秒,及时清理僵死连接 | sysctl -w net.ipv4.tcp_keepalive_time=600 |
fs.file-max | 2000000 | 系统级文件描述符总上限,默认约 9 万,高并发下不够用 | sysctl -w fs.file-max=2000000 |
fs.nr_open | 2000000 | 单进程可打开的文件数上限,必须 ≥ ulimit 的 hard 值 | sysctl -w fs.nr_open=2000000 |
vm.swappiness | 10 | 降低使用 swap 的倾向(0~100,默认 60)。数据库服务器建议设 1~10 | sysctl -w vm.swappiness=10 |
vm.dirty_ratio | 20 | 脏页占总内存比例达此值触发同步刷盘,默认 20,写密集场景可降到 10 减少抖动 | sysctl -w vm.dirty_ratio=20 |
vm.overcommit_memory | 1 | Redis 服务器必须设 1,避免 fork 时因内存检查失败而报错 | sysctl -w vm.overcommit_memory=1 |
# 一次性写入所有内核参数(Ubuntu 24.04 / Rocky Linux 9 通用)
sudo tee /etc/sysctl.d/99-performance.conf >/dev/null <<'EOF'
# 网络:连接队列与 TIME_WAIT
net.core.somaxconn = 65535
net.core.netdev_max_backlog = 250000
net.ipv4.tcp_max_syn_backlog = 65535
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_fin_timeout = 15
net.ipv4.ip_local_port_range = 1024 65535
net.ipv4.tcp_keepalive_time = 600
net.ipv4.tcp_max_tw_buckets = 262144
# 文件系统:描述符上限
fs.file-max = 2000000
fs.nr_open = 2000000
# 内存:少用 swap,控制脏页刷盘
vm.swappiness = 10
vm.dirty_ratio = 20
vm.dirty_background_ratio = 5
# Redis 服务器专用(非 Redis 机器请注释掉)
vm.overcommit_memory = 1
EOF
# 立即生效,并验证
sudo sysctl --system
sysctl net.core.somaxconn fs.file-max vm.swappiness技术提示:net.ipv4.tcp_tw_recycle已在 Linux 4.12 内核中被移除,不要再写入配置文件(会报cannot stat /proc/sys/net/ipv4/tcp_tw_recycle)。另外tcp_tw_reuse只对主动发起的出站连接生效,如果你的服务器只做被动接入(纯 Web 服务),它的实际收益有限,真正解决 TIME_WAIT 堆积要靠 upstream keepalive 长连接复用。
二、文件描述符与资源限制(ulimit)
结论:内核参数调完必须同步调 ulimit,否则进程仍被 1024 的软限制卡住,这是最常见的"改了没效果"原因。
# 查看当前限制
ulimit -n # soft 限制
ulimit -Hn # hard 限制
# 永久生效:注意 fs.nr_open 必须 ≥ 这里设置的 hard 值,否则会导致无法登录
sudo tee /etc/security/limits.d/99-performance.conf >/dev/null <<'EOF'
* soft nofile 100000
* hard nofile 100000
* soft nproc 65535
* hard nproc 65535
root soft nofile 100000
root hard nofile 100000
EOF
# systemd 管理的服务不读 limits.conf,必须单独改服务的 LimitNOFILE
sudo systemctl edit nginx
# 在打开的编辑器中写入:
# [Service]
# LimitNOFILE=100000
sudo systemctl daemon-reload && sudo systemctl restart nginx
# 验证某个进程实际生效的限制(把 PID 换成实际进程号)
cat /proc/$(pidof nginx | awk '{print $1}')/limits | grep "open files"三、Nginx 参数优化清单
结论:Nginx 优化的重点是"连接复用 + 减少磁盘 IO + 压缩传输",五个参数覆盖了大部分收益。
| 参数 | 推荐值 | 影响说明 |
|---|---|---|
worker_processes | auto | 工作进程数等于 CPU 核数,避免进程数过多导致上下文切换 |
worker_connections | 65535 | 单进程最大连接数,总并发 = worker_processes × worker_connections |
worker_rlimit_nofile | 100000 | 提升 worker 进程的文件描述符上限,需与 ulimit 匹配 |
use epoll | epoll | Linux 下必须启用 epoll 事件模型,性能远高于 select |
multi_accept on | on | 一次 accept 多个连接,提高高并发下的接入速度 |
keepalive_timeout | 30s | 长连接保持时间,太短失去复用意义,太长占用连接资源 |
keepalive_requests | 10000 | 单个长连接最多处理多少请求,防止单个连接长期占用 |
sendfile on + tcp_nopush on | on | 零拷贝发送,减少内核态到用户态的数据拷贝 |
gzip on | gzip_comp_level 5 | 文本资源压缩,减少 60%~80% 传输量,但会消耗 CPU |
open_file_cache | max=100000 inactive=60s | 缓存文件句柄与元数据,显著降低静态资源的磁盘 IO |
client_max_body_size | 按需(如 64m) | 允许上传的最大包体,太小会导致上传 413 错误 |
proxy_buffering on | on | 反向代理时缓冲后端响应,避免慢客户端拖住后端 |
# /etc/nginx/nginx.conf 核心优化片段
user nginx;
worker_processes auto;
worker_rlimit_nofile 100000;
error_log /var/log/nginx/error.log warn;
pid /run/nginx.pid;
events {
use epoll;
worker_connections 65535;
multi_accept on;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
# 日志格式增加 $request_time 与 $upstream_response_time,便于算 P99
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" rt=$request_time urt=$upstream_response_time';
access_log /var/log/nginx/access.log main buffer=32k flush=5s;
# 传输优化:零拷贝 + 长连接复用
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 30;
keepalive_requests 10000;
# 压缩:只压文本类,图片视频本身已压缩不要再压
gzip on;
gzip_comp_level 5;
gzip_min_length 1024;
gzip_buffers 16 8k;
gzip_types text/plain text/css application/json application/javascript
application/xml text/xml image/svg+xml;
gzip_vary on;
gzip_disable "msie6";
# 文件句柄缓存,静态资源多的站点收益明显
open_file_cache max=100000 inactive=60s;
open_file_cache_valid 60s;
open_file_cache_min_uses 2;
open_file_cache_errors on;
# 客户端限制
client_max_body_size 64m;
client_body_buffer_size 256k;
client_header_timeout 15s;
client_body_timeout 30s;
send_timeout 30s;
reset_timedout_connection on;
include /etc/nginx/conf.d/*.conf;
}# 改完务必校验并热加载,reload 不会断连接
sudo nginx -t && sudo systemctl reload nginx
# 验证 worker 进程数与连接上限是否生效
ps -ef | grep -c "[n]ginx: worker"
curl -s http://127.0.0.1/nginx-status四、MySQL 关键参数清单
结论:MySQL 优化 80% 的收益来自一个参数——innodb_buffer_pool_size。 把它设为物理内存的 50%~70%,其它参数都是次要的。
| 参数 | 推荐值 | 影响说明 |
|---|---|---|
innodb_buffer_pool_size | 物理内存的 50%~70%(独享数据库可到 75%) | 缓存数据与索引,命中率应 > 99%。这是影响最大的参数 |
innodb_buffer_pool_instances | 缓冲池 > 8GB 时设 4~8 | 减少缓冲池内部锁竞争 |
innodb_log_file_size | 1GB~2GB | redo log 越大,写抖动越少,但崩溃恢复时间越长 |
innodb_flush_log_at_trx_commit | 2(允许丢 1 秒数据)或 1(最高安全) | 设为 2 可大幅提升写入 TPS,宕机最多丢 1 秒事务 |
innodb_flush_method | O_DIRECT | 绕过操作系统页缓存,避免双重缓存浪费内存 |
innodb_io_capacity | SSD 设 2000~10000,机械盘设 200 | 告诉 InnoDB 存储设备的能力,影响刷脏页速度 |
max_connections | 500~2000(按实际并发) | 过大易导致内存耗尽,应配合连接池使用 |
thread_cache_size | 100 | 缓存线程,减少频繁建连的开销 |
table_open_cache | 4000 | 表句柄缓存,表多时调大 |
tmp_table_size / max_heap_table_size | 256M | 调大可减少临时表落盘 |
slow_query_log | ON,long_query_time=1 | 慢查询日志是优化索引的依据,必须开 |
character_set_server | utf8mb4 | 按需设置,避免乱码与索引长度问题 |
# /etc/mysql/mysql.conf.d/mysqld.cnf(MySQL 8.4 / MariaDB 11.4 适用)
[mysqld]
# ---- 内存:8 核 32G 独享数据库示例 ----
innodb_buffer_pool_size = 20G
innodb_buffer_pool_instances = 8
innodb_log_file_size = 2G
innodb_log_buffer_size = 64M
# ---- IO 与持久化 ----
innodb_flush_method = O_DIRECT
innodb_io_capacity = 5000
innodb_io_capacity_max = 10000
innodb_flush_log_at_trx_commit = 2
innodb_flush_neighbors = 0 # SSD 必设为 0
# ---- 连接 ----
max_connections = 1000
thread_cache_size = 100
table_open_cache = 4000
wait_timeout = 300
interactive_timeout = 300
# ---- 临时表与排序 ----
tmp_table_size = 256M
max_heap_table_size = 256M
sort_buffer_size = 4M
join_buffer_size = 4M
read_buffer_size = 2M
# ---- 慢查询(必开) ----
slow_query_log = ON
slow_query_log_file = /var/log/mysql/slow.log
long_query_time = 1
log_queries_not_using_indexes = OFF
min_examined_row_limit = 100
character_set_server = utf8mb4
collation_server = utf8mb4_0900_ai_ci# 应用配置并验证
sudo systemctl restart mysql
mysql -u root -p -e "SHOW VARIABLES LIKE 'innodb_buffer_pool_size'; SHOW VARIABLES LIKE 'max_connections';"
# 关键验证:InnoDB 缓冲池命中率应 > 99%
mysql -u root -p -e "
SELECT ROUND((1 - (SELECT VARIABLE_VALUE FROM performance_schema.global_status
WHERE VARIABLE_NAME='Innodb_buffer_pool_reads')
/ (SELECT VARIABLE_VALUE FROM performance_schema.global_status
WHERE VARIABLE_NAME='Innodb_buffer_pool_read_requests')) * 100, 2) AS hit_rate_percent;"
# 用 mysqldumpslow 找出最该优化的慢查询
mysqldumpslow -s t -t 10 /var/log/mysql/slow.log五、PHP-FPM 参数与 pm.max_children 计算
结论:pm.max_children 不能拍脑袋设,要用公式算——max_children ≈ 可用内存 / 单个 PHP 进程平均内存。 设太大会 OOM,设太小会 502。
# 第一步:测出单个 PHP-FPM 子进程平均占用内存(单位 MB)
ps -eo size,rss,comm --sort -rss | grep php-fpm | awk '{s+=$2; n++} END{printf "总内存 %.0f MB, 进程数 %d, 平均 %.1f MB\n", s/1024, n, s/n/1024}'
# 第二步:套用公式(示例:16G 内存,系统与其它服务占 4G,单进程 40MB)
# max_children = (16*1024 - 4*1024) / 40 ≈ 307,取保守值 250# /etc/php/8.3/fpm/pool.d/www.conf
[www]
user = www-data
group = www-data
# 用 dynamic 动态模式(小内存机器用 ondemand)
pm = dynamic
pm.max_children = 250 ; 按上面公式算出,硬上限
pm.start_servers = 20 ; 启动时进程数,建议 = CPU 核数 × 4
pm.min_spare_servers = 10 ; 最小空闲进程
pm.max_spare_servers = 40 ; 最大空闲进程,超过会被回收
pm.max_requests = 2000 ; 单进程处理多少请求后重启,防内存泄漏
; 慢日志:超过 5 秒的脚本记录下来,是定位性能问题的利器
slowlog = /var/log/php-fpm/www-slow.log
request_slowlog_timeout = 5s
; 状态页,配合 Nginx 查看实时排队情况
pm.status_path = /fpm-status
; 防超大脚本
php_admin_value[memory_limit] = 256M# 重启并验证
sudo php-fpm8.3 -t && sudo systemctl restart php8.3-fpm
# 观察是否出现排队(listen queue > 0 说明 max_children 不够)
curl -s http://127.0.0.1/fpm-status
# 关键字段:listen queue(排队数)、active processes(活跃进程)、max children reached(达到上限次数)如果 max children reached 计数器持续增长,说明进程数不够,需要调大 pm.max_children 或优化 PHP 代码(更推荐后者)。
六、Redis 参数清单
结论:Redis 优化的核心不是性能而是内存——设好 maxmemory 与淘汰策略,避免写满导致缓存雪崩。
| 参数 | 推荐值 | 影响说明 |
|---|---|---|
maxmemory | 物理内存的 60%~70% | 必须设置,否则 Redis 会吃满内存被 OOM Killer 杀掉 |
maxmemory-policy | allkeys-lru(纯缓存)或 noeviction(含持久化关键数据) | 决定内存满时怎么淘汰。纯缓存场景用 allkeys-lru |
save | 缓存场景可关闭 RDB,或延长间隔 | RDB 持久化 fork 时会短暂阻塞,大数据量下影响明显 |
appendonly | 关键数据设 yes,纯缓存设 no | AOF 更安全但写入开销更大 |
tcp-backlog | 511(配合 somaxconn 调大) | 高并发接入队列 |
timeout | 0(不主动断开) | 由客户端池管理连接 |
lazyfree-lazy-eviction | yes | 异步释放内存,避免大 key 删除时阻塞主线程 |
vm.overcommit_memory | 1(系统参数) | 必须设为 1,否则 fork 可能失败 |
# /etc/redis/redis.conf 关键片段
maxmemory 8gb
maxmemory-policy allkeys-lru
maxmemory-samples 5
# 持久化:纯缓存可关闭,关键数据开启 AOF
save 900 1
appendonly no
# 网络
tcp-backlog 511
timeout 0
tcp-keepalive 300
# 异步释放,避免删大 key 时卡住
lazyfree-lazy-eviction yes
lazyfree-lazy-expire yes
lazyfree-lazy-server-del yes
replica-lazy-flush yes
# 危险命令重命名,防止误操作
rename-command FLUSHALL ""
rename-command FLUSHDB ""
rename-command KEYS ""# 应用并验证
sudo systemctl restart redis-server
redis-cli -a 'RedisPass2026' INFO memory | grep -E "used_memory_human|maxmemory_human|maxmemory_policy"
redis-cli -a 'RedisPass2026' INFO stats | grep -E "keyspace_hits|keyspace_misses"
# 计算命中率:应 > 95%,低于 90% 说明缓存设计有问题
# 命中率 = hits / (hits + misses)
企业QQ咨询




