服务器性能优化技巧

结论: 服务器性能优化的见效顺序是:先加缓存,再调参数,最后才加硬件。本文给出一张可以直接照抄的参数清单——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 服务器参数 → 操作系统内核参数 → 硬件扩容。前两项的收益通常是后三项的十倍以上,却常被跳过。

先花五分钟采集基线:

bash
# 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.somaxconn65535全连接队列上限,默认 128。高并发时队列溢出会导致连接被丢弃,Nginx 日志出现 connection resetsysctl -w net.core.somaxconn=65535
net.core.netdev_max_backlog250000网卡接收队列长度,高流量下防止丢包sysctl -w net.core.netdev_max_backlog=250000
net.ipv4.tcp_max_syn_backlog65535半连接(SYN)队列长度,防 SYN Flood 时误伤正常用户sysctl -w net.ipv4.tcp_max_syn_backlog=65535
net.ipv4.tcp_tw_reuse1允许复用 TIME_WAIT 套接字用于出站连接,显著降低 TIME_WAIT 堆积sysctl -w net.ipv4.tcp_tw_reuse=1
net.ipv4.tcp_fin_timeout15TIME_WAIT 回收时间从默认 60 秒降到 15 秒sysctl -w net.ipv4.tcp_fin_timeout=15
net.ipv4.ip_local_port_range1024 65535可用本地端口范围,决定单机对外最大连接数sysctl -w net.ipv4.ip_local_port_range="1024 65535"
net.ipv4.tcp_keepalive_time600TCP 保活探测间隔从 7200 秒降到 600 秒,及时清理僵死连接sysctl -w net.ipv4.tcp_keepalive_time=600
fs.file-max2000000系统级文件描述符总上限,默认约 9 万,高并发下不够用sysctl -w fs.file-max=2000000
fs.nr_open2000000单进程可打开的文件数上限,必须 ≥ ulimit 的 hard 值sysctl -w fs.nr_open=2000000
vm.swappiness10降低使用 swap 的倾向(0~100,默认 60)。数据库服务器建议设 1~10sysctl -w vm.swappiness=10
vm.dirty_ratio20脏页占总内存比例达此值触发同步刷盘,默认 20,写密集场景可降到 10 减少抖动sysctl -w vm.dirty_ratio=20
vm.overcommit_memory1Redis 服务器必须设 1,避免 fork 时因内存检查失败而报错sysctl -w vm.overcommit_memory=1
bash
# 一次性写入所有内核参数(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 的软限制卡住,这是最常见的"改了没效果"原因。

bash
# 查看当前限制
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_processesauto工作进程数等于 CPU 核数,避免进程数过多导致上下文切换
worker_connections65535单进程最大连接数,总并发 = worker_processes × worker_connections
worker_rlimit_nofile100000提升 worker 进程的文件描述符上限,需与 ulimit 匹配
use epollepollLinux 下必须启用 epoll 事件模型,性能远高于 select
multi_accept onon一次 accept 多个连接,提高高并发下的接入速度
keepalive_timeout30s长连接保持时间,太短失去复用意义,太长占用连接资源
keepalive_requests10000单个长连接最多处理多少请求,防止单个连接长期占用
sendfile on + tcp_nopush onon零拷贝发送,减少内核态到用户态的数据拷贝
gzip ongzip_comp_level 5文本资源压缩,减少 60%~80% 传输量,但会消耗 CPU
open_file_cachemax=100000 inactive=60s缓存文件句柄与元数据,显著降低静态资源的磁盘 IO
client_max_body_size按需(如 64m)允许上传的最大包体,太小会导致上传 413 错误
proxy_buffering onon反向代理时缓冲后端响应,避免慢客户端拖住后端
nginx
# /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;
}
bash
# 改完务必校验并热加载,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_size1GB~2GBredo log 越大,写抖动越少,但崩溃恢复时间越长
innodb_flush_log_at_trx_commit2(允许丢 1 秒数据)或 1(最高安全)设为 2 可大幅提升写入 TPS,宕机最多丢 1 秒事务
innodb_flush_methodO_DIRECT绕过操作系统页缓存,避免双重缓存浪费内存
innodb_io_capacitySSD 设 2000~10000,机械盘设 200告诉 InnoDB 存储设备的能力,影响刷脏页速度
max_connections500~2000(按实际并发)过大易导致内存耗尽,应配合连接池使用
thread_cache_size100缓存线程,减少频繁建连的开销
table_open_cache4000表句柄缓存,表多时调大
tmp_table_size / max_heap_table_size256M调大可减少临时表落盘
slow_query_logON,long_query_time=1慢查询日志是优化索引的依据,必须开
character_set_serverutf8mb4按需设置,避免乱码与索引长度问题
ini
# /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
bash
# 应用配置并验证
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。

bash
# 第一步:测出单个 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
ini
# /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
bash
# 重启并验证
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-policyallkeys-lru(纯缓存)或 noeviction(含持久化关键数据)决定内存满时怎么淘汰。纯缓存场景用 allkeys-lru
save缓存场景可关闭 RDB,或延长间隔RDB 持久化 fork 时会短暂阻塞,大数据量下影响明显
appendonly关键数据设 yes,纯缓存设 noAOF 更安全但写入开销更大
tcp-backlog511(配合 somaxconn 调大)高并发接入队列
timeout0(不主动断开)由客户端池管理连接
lazyfree-lazy-evictionyes异步释放内存,避免大 key 删除时阻塞主线程
vm.overcommit_memory1(系统参数)必须设为 1,否则 fork 可能失败
conf
# /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     ""
bash
# 应用并验证
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)