一、调优前的准备工作

1.1 了解你的业务场景

在动手之前,先搞清楚你的服务器是干什么的。不同场景的调优方向完全不同:

  • 高并发Web服务:重点调网络栈,特别是TCP连接相关参数

  • 数据库服务器:重点调内存和I/O,要减少swap,优化页面缓存

  • 消息队列:网络和I/O都要调,还要注意文件描述符限制

  • 计算密集型服务:重点调CPU调度策略和NUMA配置

我见过不少人拿着网上的调优参数无脑复制,结果越调越差。比如把TCP缓冲区调得巨大,结果内存吃紧触发OOM。所以一定要搞清楚业务特点再动手。

1.2 建立性能基线

调优之前先记录当前的性能数据,不然调完了你都不知道有没有效果。我一般会采集这些指标:

# 记录系统基本信息
uname -a > /tmp/baseline/system_info.txt
cat /proc/cpuinfo | grep "model name" | head -1 >> /tmp/baseline/system_info.txt
free -h >> /tmp/baseline/system_info.txt
df -h >> /tmp/baseline/system_info.txt

# 网络性能基线
ss -s > /tmp/baseline/network_baseline.txt
cat /proc/net/sockstat >> /tmp/baseline/network_baseline.txt

# 内存性能基线
vmstat 1 10 > /tmp/baseline/memory_baseline.txt
cat /proc/meminfo > /tmp/baseline/meminfo_baseline.txt

# I/O性能基线
iostat -x 1 10 > /tmp/baseline/io_baseline.txt

# CPU性能基线
mpstat -P ALL 1 10 > /tmp/baseline/cpu_baseline.txt

1.3 查看当前内核参数

# 导出所有当前的sysctl参数作为备份
sysctl -a > /tmp/baseline/sysctl_backup.txt 2>/dev/null

# 查看特定参数
sysctl net.core.somaxconn
sysctl vm.swappiness
sysctl net.ipv4.tcp_max_syn_backlog

1.4 确认内核版本

不同版本的内核支持的参数不一样,有些新参数在老内核上根本不存在。2025年主流发行版的内核版本:

  • RHEL/Rocky 9.x:内核5.14.x

  • Ubuntu 24.04 LTS:内核6.8.x

  • Debian 12:内核6.1.x

  • 最新upstream内核:6.12.x

uname -r
# 输出类似:6.8.0-45-generic

# 查看内核编译时间
cat /proc/version

二、网络子系统调优

网络调优是最常见的,也是效果最明显的。特别是高并发场景,默认参数简直就是性能杀手。

2.1 TCP连接队列调优

先说两个最重要的队列参数:

# 半连接队列(SYN队列)大小
# 默认值通常是128或256,高并发下远远不够
sysctl -w net.ipv4.tcp_max_syn_backlog=65535

# 全连接队列(Accept队列)大小
# 默认值128,这个值决定了listen()的backlog上限
sysctl -w net.core.somaxconn=65535

这两个参数我见过无数次因为设置太小导致的问题。症状就是新连接被拒绝,netstat能看到大量SYN_RECV状态的连接。

怎么确认是不是队列溢出?

# 查看SYN队列溢出次数
netstat -s | grep "SYNs to LISTEN"
# 或者
cat /proc/net/netstat | grep -i listen

# 查看Accept队列溢出次数
netstat -s | grep "overflowed"
# 或者
ss -ltn  # 看Recv-Q和Send-Q

如果这些数字在持续增长,说明队列确实不够用了。

实战案例:我们有个电商活动的API服务器,平时流量不大,大促期间QPS从500飙到5万。上线前我就把这两个参数调到65535,另外应用层的listen backlog也要改:

# Nginx配置
server {
    listen 80 backlog=65535;
    ...
}
// Java应用(Spring Boot)
server.tomcat.accept-count=65535

2.2 TCP缓冲区调优

TCP的收发缓冲区大小直接影响吞吐量,特别是在高延迟网络(比如跨国专线)上效果更明显。

# TCP读缓冲区:最小值、默认值、最大值(单位:字节)
sysctl -w net.ipv4.tcp_rmem="4096 87380 16777216"

# TCP写缓冲区:最小值、默认值、最大值
sysctl -w net.ipv4.tcp_wmem="4096 65536 16777216"

# 所有协议的默认读缓冲区
sysctl -w net.core.rmem_default=262144
sysctl -w net.core.rmem_max=16777216

# 所有协议的默认写缓冲区
sysctl -w net.core.wmem_default=262144
sysctl -w net.core.wmem_max=16777216

这里有个常见误区:不是缓冲区越大越好。缓冲区太大会:

  1. 消耗更多内存

  2. 增加延迟(数据在缓冲区里堆积)

  3. 影响拥塞控制算法的效果

我的经验是:

  • 内网服务:保持默认或适当增大即可

  • 跨机房/跨地域:根据带宽延迟积(BDP)计算

带宽延迟积的计算公式:

BDP = 带宽(bps) × RTT(秒)

比如100Mbps带宽,RTT 50ms:

BDP = 100,000,000 × 0.05 = 5,000,000 bits = 625,000 bytes

所以缓冲区至少要设到625KB以上才能充分利用带宽。

2.3 TCP连接复用和TIME_WAIT优化

TIME_WAIT状态是TCP协议的正常行为,但在高并发短连接场景下会成为问题。一个端口被TIME_WAIT占着,2MSL(默认60秒)内不能复用。

# 允许TIME_WAIT的socket重用
sysctl -w net.ipv4.tcp_tw_reuse=1

# TIME_WAIT bucket数量
# 默认值太小,高并发下会报"time wait bucket table overflow"
sysctl -w net.ipv4.tcp_max_tw_buckets=262144

# 开启TCP连接的timestamps(tcp_tw_reuse的前提条件)
sysctl -w net.ipv4.tcp_timestamps=1

注意:tcp_tw_recycle这个参数在内核4.12之后被移除了,不要再用了。原因是它在NAT环境下会导致连接被误杀。

实战案例:有次排查一个诡异的问题,客户端偶尔连接超时。查了半天发现是后端服务器连接Redis的TIME_WAIT太多,把本地端口(默认32768-60999,大约2.8万个)占满了。解决方案:

# 扩大本地端口范围
sysctl -w net.ipv4.ip_local_port_range="1024 65535"

# 配合tcp_tw_reuse
sysctl -w net.ipv4.tcp_tw_reuse=1

2.4 TCP keepalive调优

长连接场景下,keepalive参数很重要。默认值对生产环境来说太保守了:

# 连接空闲多久后开始发送keepalive探测(默认7200秒,太长了)
sysctl -w net.ipv4.tcp_keepalive_time=600

# 探测间隔(默认75秒)
sysctl -w net.ipv4.tcp_keepalive_intvl=30

# 探测次数,超过这个次数没响应就断开(默认9次)
sysctl -w net.ipv4.tcp_keepalive_probes=3

按上面的配置,一个死连接最多600+30×3=690秒后会被清理。

但要注意,内核级别的keepalive只对开启了SO_KEEPALIVE选项的socket生效。很多应用会自己实现应用层的心跳,比如Redis、MySQL的ping。两者不冲突,看你怎么用。

2.5 拥塞控制算法

Linux内核支持多种TCP拥塞控制算法,选择合适的算法能显著提升网络性能。

# 查看当前使用的拥塞控制算法
sysctl net.ipv4.tcp_congestion_control

# 查看系统支持的所有算法
sysctl net.ipv4.tcp_available_congestion_control

# 查看已加载的算法模块
sysctl net.ipv4.tcp_allowed_congestion_control

2025年主流选择:

  1. BBR(推荐):Google开发的算法,基于带宽和RTT建模,在丢包网络中表现优异

  2. CUBIC:Linux默认算法,基于丢包的传统算法,适合低延迟网络

  3. BBRv2/BBRv3:BBR的改进版本,解决了与CUBIC竞争不公平的问题

启用BBR:

# 加载BBR模块
modprobe tcp_bbr

# 设置拥塞控制算法为BBR
sysctl -w net.ipv4.tcp_congestion_control=bbr

# 设置默认队列规则为fq(BBR需要)
sysctl -w net.core.default_qdisc=fq

实战案例:我们有个跨太平洋的专线,RTT在150-200ms,丢包率大约0.5%。换用BBR后,文件传输速度从原来的30Mbps提升到接近带宽上限的90Mbps。

2.6 网络设备队列调优

网卡的队列长度也会影响性能:

# 网络设备的接收队列长度
sysctl -w net.core.netdev_max_backlog=65535

# 网络设备的发送队列长度(这个通过ip命令调)
ip link set eth0 txqueuelen 10000

netdev_max_backlog在高速网络(10G、25G、100G)上特别重要。如果这个值太小,网卡收包速度超过内核处理速度时就会丢包。

查看是否有丢包:

# 查看网卡统计
ip -s link show eth0

# 查看是否有backlog溢出
cat /proc/net/softnet_stat
# 第二列是dropped,第三列是time_squeeze(CPU忙不过来)

2.7 完整的网络调优配置

把上面的参数整理成一个完整的配置文件:

# /etc/sysctl.d/99-network-tuning.conf

# TCP连接队列
net.core.somaxconn = 65535
net.ipv4.tcp_max_syn_backlog = 65535

# TCP缓冲区
net.ipv4.tcp_rmem = 4096 87380 16777216
net.ipv4.tcp_wmem = 4096 65536 16777216
net.core.rmem_default = 262144
net.core.rmem_max = 16777216
net.core.wmem_default = 262144
net.core.wmem_max = 16777216

# TCP连接优化
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_max_tw_buckets = 262144
net.ipv4.tcp_timestamps = 1
net.ipv4.ip_local_port_range = 1024 65535
net.ipv4.tcp_fin_timeout = 30
net.ipv4.tcp_slow_start_after_idle = 0

# TCP keepalive
net.ipv4.tcp_keepalive_time = 600
net.ipv4.tcp_keepalive_intvl = 30
net.ipv4.tcp_keepalive_probes = 3

# 拥塞控制
net.core.default_qdisc = fq
net.ipv4.tcp_congestion_control = bbr

# 网络设备队列
net.core.netdev_max_backlog = 65535

# 开启TCP Fast Open(减少握手延迟)
net.ipv4.tcp_fastopen = 3

# SYN flood防护(适当放宽)
net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_max_syn_backlog = 65535

# 路由缓存
net.ipv4.route.max_size = 2147483647

应用配置:

sysctl -p /etc/sysctl.d/99-network-tuning.conf

三、内存子系统调优

内存调优主要是让系统更好地利用物理内存,减少不必要的swap和OOM。

3.1 Swap使用策略

# swappiness控制内核使用swap的倾向
# 范围0-100,值越大越倾向使用swap
# 默认值60,对服务器来说太高了

# 数据库服务器(尽量不用swap)
sysctl -w vm.swappiness=1

# 一般服务器
sysctl -w vm.swappiness=10

# 内存充足的服务器(完全不用swap)
sysctl -w vm.swappiness=0

为什么不直接禁用swap?因为在极端情况下,有swap比没有好。没有swap时内存耗尽直接触发OOM Killer,有swap至少还有点喘息空间。

但是,如果你的服务对延迟敏感(比如Redis、实时交易系统),swap会带来不可预测的延迟抖动,这时候宁可OOM也不要swap:

# 完全禁用swap
swapoff -a
# 从fstab中注释掉swap行,防止重启后恢复

3.2 内存过量提交策略

Linux默认允许内存过量提交(overcommit),即允许申请的内存总量超过物理内存。这在大多数场景下没问题,因为程序申请的内存不一定全用。

# 查看当前策略
sysctl vm.overcommit_memory
# 0:启发式策略(默认),明显不合理的申请会被拒绝
# 1:总是允许,适合某些特殊应用(如Redis做持久化时fork)
# 2:不允许超过swap+物理内存*overcommit_ratio

# 查看/设置比率
sysctl vm.overcommit_ratio
# 默认50,表示可用虚拟内存 = swap + 物理内存 * 50%

Redis的特殊情况:Redis在做RDB持久化或AOF重写时会fork子进程,fork需要复制页表,如果overcommit_memory=0可能会失败。Redis官方建议:

sysctl -w vm.overcommit_memory=1

3.3 脏页刷新策略

脏页是指被修改过但还没写到磁盘的内存页。脏页策略影响数据安全性和I/O性能的平衡。

# 脏页占总内存的百分比,超过这个值后台开始刷盘
sysctl -w vm.dirty_background_ratio=5

# 脏页占总内存的百分比,超过这个值前台开始刷盘(阻塞写操作)
sysctl -w vm.dirty_ratio=10

# 脏页存活的最长时间(centiseconds,1/100秒)
# 默认3000(30秒),太长了
sysctl -w vm.dirty_expire_centisecs=1500

# 刷盘进程唤醒间隔
sysctl -w vm.dirty_writeback_centisecs=500

对于数据安全性要求高的场景(数据库),可以调得更激进:

vm.dirty_background_ratio = 3
vm.dirty_ratio = 5
vm.dirty_expire_centisecs = 500

这样脏页会更快地刷到磁盘,减少数据丢失风险,但I/O压力会增加。

3.4 大页内存(Huge Pages)

传统的内存页是4KB,对于大内存应用(数据库、JVM)来说,页表会非常大,TLB(Translation Lookaside Buffer)命中率低。

启用大页(2MB或1GB)可以:

  • 减少页表大小

  • 提高TLB命中率

  • 减少页面错误

# 查看大页信息
cat /proc/meminfo | grep Huge
# HugePages_Total:       0  总共配置的大页数量
# HugePages_Free:        0  空闲大页
# HugePages_Rsvd:        0  已预留但未分配的大页
# Hugepagesize:       2048 kB  大页大小

# 配置大页数量(比如给数据库预留16GB)
# 16GB / 2MB = 8192
sysctl -w vm.nr_hugepages=8192

# 或者在启动参数中配置
# /etc/default/grub:
# GRUB_CMDLINE_LINUX="hugepagesz=2M hugepages=8192"

实战案例:给MySQL配置大页:

# 1. 配置大页
echo 4096 > /proc/sys/vm/nr_hugepages

# 2. 创建大页挂载点
mkdir /dev/hugepages
mount -t hugetlbfs hugetlbfs /dev/hugepages

# 3. 设置MySQL配置
# /etc/my.cnf
[mysqld]
large-pages

3.5 透明大页(Transparent Huge Pages)

透明大页是自动管理的大页,不需要应用配合。但是它有个问题:后台的khugepaged进程会尝试合并小页为大页,这个过程可能导致延迟抖动。

对于延迟敏感的应用(Redis、数据库、实时系统),建议禁用:

# 查看当前状态
cat /sys/kernel/mm/transparent_hugepage/enabled
# [always] madvise never

# 禁用透明大页
echo never > /sys/kernel/mm/transparent_hugepage/enabled
echo never > /sys/kernel/mm/transparent_hugepage/defrag

永久禁用需要在启动参数中配置:

# /etc/default/grub
GRUB_CMDLINE_LINUX="transparent_hugepage=never"

3.6 NUMA优化

NUMA(Non-Uniform Memory Access)架构下,CPU访问本地内存比远程内存快。多路服务器一般都是NUMA架构。

# 查看NUMA拓扑
numactl --hardware
# available: 2 nodes (0-1)
# node 0 cpus: 0 1 2 3 4 5 6 7 16 17 18 19 20 21 22 23
# node 0 size: 131072 MB
# node 1 cpus: 8 9 10 11 12 13 14 15 24 25 26 27 28 29 30 31
# node 1 size: 131072 MB

# 查看内存分配情况
numastat

# 查看进程的NUMA内存分布
numastat -p <pid>

NUMA相关的内核参数:

# 控制zone reclaim行为
# 0:从远程节点分配内存(推荐)
# 1:优先回收本地内存
sysctl -w vm.zone_reclaim_mode=0

zone_reclaim_mode=1在某些场景下会导致性能问题,因为它会优先回收本地内存(包括页面缓存),而不是使用远程节点的空闲内存。对于大多数应用来说,保持为0更好。

实战案例:给关键进程绑定NUMA节点:

# 让进程只使用node 0的CPU和内存
numactl --cpunodebind=0 --membind=0 ./your_application

# 或者使用cgroup配置(适合容器环境)
# 在docker中
docker run --cpuset-cpus="0-7" --cpuset-mems="0" your_image

3.7 OOM调优

当内存耗尽时,OOM Killer会选择并杀死进程。可以调整进程被选中的优先级:

# 查看进程的OOM分数(分数越高越容易被杀)
cat /proc/<pid>/oom_score

# 调整进程的OOM分数调整值(-1000到1000)
echo -500 > /proc/<pid>/oom_score_adj

# -1000表示永不被OOM杀死(关键进程如数据库可以这样设置)
echo -1000 > /proc/<pid>/oom_score_adj

在systemd服务中配置:

# /etc/systemd/system/your-service.service
[Service]
OOMScoreAdjust=-1000

另外还有个panic_on_oom参数,出现OOM时触发kernel panic。在某些场景下(比如集群环境),让节点重启比进程被杀更安全:

# OOM时触发panic(谨慎使用)
sysctl -w vm.panic_on_oom=1

3.8 完整的内存调优配置

# /etc/sysctl.d/99-memory-tuning.conf

# Swap策略(根据业务调整)
vm.swappiness = 10

# 脏页刷新策略
vm.dirty_background_ratio = 5
vm.dirty_ratio = 10
vm.dirty_expire_centisecs = 1500
vm.dirty_writeback_centisecs = 500

# 内存过量提交
vm.overcommit_memory = 0
vm.overcommit_ratio = 50

# NUMA
vm.zone_reclaim_mode = 0

# 大页相关(按需配置)
# vm.nr_hugepages = 8192

# 内存回收相关
vm.min_free_kbytes = 524288
vm.vfs_cache_pressure = 100

# 防止OOM时内核panic(生产环境建议保持0)
vm.panic_on_oom = 0

四、I/O子系统调优

磁盘I/O往往是性能瓶颈所在,特别是传统机械硬盘。调优目标是减少不必要的I/O、提高I/O效率。

4.1 I/O调度器选择

Linux支持多种I/O调度器,不同场景选择不同:

# 查看当前块设备的调度器
cat /sys/block/sda/queue/scheduler
# [mq-deadline] kyber bfq none

# 临时修改调度器
echo mq-deadline > /sys/block/sda/queue/scheduler

各调度器特点:

  • none/noop:不做任何调度,直接把请求发给设备。适合NVMe SSD和虚拟机

  • mq-deadline:保证请求在一定时间内被处理。适合数据库等延迟敏感的应用

  • kyber:低开销的调度器,适合快速设备(SSD)

  • bfq:公平调度,适合桌面环境和需要I/O隔离的场景

我的建议:

  • NVMe SSD:none或mq-deadline

  • SATA SSD:mq-deadline

  • HDD:mq-deadline或bfq

  • 虚拟机:none(调度由宿主机处理)

永久配置:

# /etc/udev/rules.d/60-io-scheduler.rules
# NVMe用none
ACTION=="add|change", KERNEL=="nvme[0-9]*", ATTR{queue/scheduler}="none"
# SATA/SAS用mq-deadline
ACTION=="add|change", KERNEL=="sd[a-z]", ATTR{queue/scheduler}="mq-deadline"

4.2 I/O队列深度

队列深度决定了同时能有多少I/O请求在处理中:

# 查看当前队列深度
cat /sys/block/sda/queue/nr_requests
# 默认通常是128或256

# 对于高性能SSD,可以适当增加
echo 512 > /sys/block/nvme0n1/queue/nr_requests

# 对于HDD,增加队列深度意义不大,保持默认即可

4.3 预读设置

预读(read-ahead)是指读取数据时预先读取后续的数据块到缓存中。对于顺序读取场景非常有效。

# 查看当前预读值(单位:512字节扇区)
cat /sys/block/sda/queue/read_ahead_kb
# 默认128KB

# 对于大文件顺序读取(如视频流、大数据处理),可以增大
echo 4096 > /sys/block/sda/queue/read_ahead_kb

# 对于随机读取为主的场景(数据库),可以减小
echo 16 > /sys/block/sda/queue/read_ahead_kb

# 或者用blockdev命令(单位:512字节扇区)
blockdev --setra 8192 /dev/sda  # 设置为4MB

4.4 文件系统挂载选项

挂载选项对I/O性能影响很大:

# /etc/fstab示例
# 高性能SSD挂载选项
/dev/nvme0n1p1 /data ext4 noatime,nodiratime,discard,errors=remount-ro 0 2

# 数据库分区挂载选项(XFS更适合数据库)
/dev/sdb1 /var/lib/mysql xfs noatime,nodiratime,nobarrier,logbufs=8,logbsize=256k 0 2

关键选项解释:

  • noatime:不更新文件访问时间,减少写操作

  • nodiratime:不更新目录访问时间

  • discard:启用TRIM(SSD专用)

  • nobarrier:禁用写屏障,提高性能但断电可能丢数据(需要有UPS或RAID卡带电池)

  • data=writeback:ext4选项,不记录数据到journal,只记录元数据

实战案例:给MySQL优化挂载选项

# 假设MySQL数据目录在/var/lib/mysql,使用XFS
# 1. 创建XFS文件系统时指定参数
mkfs.xfs -f -d agcount=64 -l size=512m,lazy-count=1 /dev/sdb1

# 2. 挂载选项
mount -o noatime,nodiratime,logbufs=8,logbsize=256k,allocsize=16m /dev/sdb1 /var/lib/mysql

4.5 AIO和Direct I/O

对于数据库等需要绕过页面缓存直接操作磁盘的应用,需要调整相关参数:

# 异步I/O请求的最大数量
cat /proc/sys/fs/aio-max-nr
# 默认65536,数据库可能需要更多

sysctl -w fs.aio-max-nr=1048576

4.6 文件描述符限制

高并发应用会打开大量文件和socket,默认限制往往不够:

# 查看系统级别的文件描述符限制
cat /proc/sys/fs/file-max
# 调整
sysctl -w fs.file-max=2097152

# 查看当前使用量
cat /proc/sys/fs/file-nr
# 第一列是已分配,第二列是已分配但未使用,第三列是最大值

# 进程级别的限制在/etc/security/limits.conf配置
# /etc/security/limits.conf
* soft nofile 1048576
* hard nofile 1048576
* soft nproc 65535
* hard nproc 65535

# root用户也要单独配置
root soft nofile 1048576
root hard nofile 1048576

systemd服务的限制在service文件中配置:

[Service]
LimitNOFILE=1048576
LimitNPROC=65535

4.7 inotify限制

需要监控大量文件变化的应用(如日志收集、文件同步)可能需要调整inotify限制:

# 每个用户可以创建的inotify实例数
sysctl -w fs.inotify.max_user_instances=8192

# 每个inotify实例可以监控的文件数
sysctl -w fs.inotify.max_user_watches=524288

4.8 完整的I/O调优配置

# /etc/sysctl.d/99-io-tuning.conf

# 文件描述符
fs.file-max = 2097152
fs.aio-max-nr = 1048576

# inotify
fs.inotify.max_user_instances = 8192
fs.inotify.max_user_watches = 524288

# 页面缓存相关
vm.vfs_cache_pressure = 50
vm.dirty_background_ratio = 5
vm.dirty_ratio = 10

配合udev规则设置调度器:

# /etc/udev/rules.d/60-io-scheduler.rules
ACTION=="add|change", KERNEL=="nvme[0-9]*", ATTR{queue/scheduler}="none"
ACTION=="add|change", KERNEL=="sd[a-z]", ATTR{queue/scheduler}="mq-deadline"
ACTION=="add|change", KERNEL=="nvme[0-9]*", ATTR{queue/nr_requests}="1024"
ACTION=="add|change", KERNEL=="nvme[0-9]*", ATTR{queue/read_ahead_kb}="128"

五、进程调度调优

进程调度决定了CPU时间如何分配给各个进程,调优目标是让重要进程获得足够的CPU资源。

5.1 CFS调度器参数

Linux默认使用CFS(Completely Fair Scheduler)调度器:

# 调度周期,单位微秒
cat /proc/sys/kernel/sched_latency_ns
# 默认6000000(6ms)

# 最小调度粒度
cat /proc/sys/kernel/sched_min_granularity_ns
# 默认750000(0.75ms)

# 唤醒粒度
cat /proc/sys/kernel/sched_wakeup_granularity_ns

对于延迟敏感的应用,可以减小这些值:

sysctl -w kernel.sched_latency_ns=4000000
sysctl -w kernel.sched_min_granularity_ns=500000
sysctl -w kernel.sched_wakeup_granularity_ns=2000000

5.2 进程优先级调整

# 使用nice调整优先级(-20到19,越小优先级越高)
nice -n -10 ./your_important_app

# 对已运行的进程调整
renice -n -10 -p <pid>

# 使用chrt设置实时优先级(需要root)
# SCHED_FIFO:先进先出,同优先级不抢占
chrt -f 50 ./your_realtime_app
# SCHED_RR:轮转,同优先级时间片轮转
chrt -r 50 ./your_realtime_app

# 查看进程的调度策略和优先级
chrt -p <pid>

5.3 CPU亲和性设置

把进程绑定到特定CPU核心可以提高缓存命中率:

# 使用taskset绑定CPU
# 绑定到CPU 0-3
taskset -c 0-3 ./your_app

# 对已运行的进程
taskset -pc 0-3 <pid>

# 或者用CPU掩码
taskset 0x0f ./your_app  # 0x0f = 00001111 = CPU 0-3

5.4 IRQ亲和性

网卡中断处理会消耗CPU资源,把中断绑定到特定核心可以避免干扰业务进程:

# 查看中断分布
cat /proc/interrupts

# 设置IRQ亲和性
# 找到网卡的IRQ号
grep eth0 /proc/interrupts
# 假设是42
echo 0x0f > /proc/irq/42/smp_affinity  # 绑定到CPU 0-3

# 现代网卡有多队列,每个队列一个IRQ
# 可以用irqbalance自动平衡
systemctl start irqbalance
# 或者手动配置
# 禁用irqbalance
systemctl stop irqbalance
# 手动均匀分布

对于高性能网络应用,可能需要禁用irqbalance,手动精确控制中断分布。

5.5 cgroup资源限制

使用cgroup可以对进程组进行资源限制和隔离:

# cgroup v2(现代发行版默认)
# 创建cgroup
mkdir /sys/fs/cgroup/myapp

# 限制CPU使用(50%)
echo"50000 100000" > /sys/fs/cgroup/myapp/cpu.max

# 限制内存(2GB)
echo $((2*1024*1024*1024)) > /sys/fs/cgroup/myapp/memory.max

# 将进程加入cgroup
echo <pid> > /sys/fs/cgroup/myapp/cgroup.procs

# 或者在systemd中配置
[Service]
CPUQuota=50%
MemoryMax=2G

5.6 关键进程的调度优化脚本

这是我常用的一个脚本,用于优化关键进程的调度:

#!/bin/bash
# optimize_process.sh - 优化关键进程的调度参数

PROCESS_NAME=$1
if [ -z "$PROCESS_NAME" ]; then
    echo"Usage: $0 <process_name>"
    exit 1
fi

# 找到进程PID
PIDS=$(pgrep -f "$PROCESS_NAME")
if [ -z "$PIDS" ]; then
    echo"Process not found: $PROCESS_NAME"
    exit 1
fi

for PID in$PIDS; do
    echo"Optimizing PID: $PID"

    # 设置nice值
    renice -n -10 -p $PID 2>/dev/null

    # 设置OOM分数调整
    echo -500 > /proc/$PID/oom_score_adj 2>/dev/null

    # 绑定到特定CPU(这里绑定到物理核心)
    # 获取NUMA节点0的CPU列表
    CPUS=$(numactl --hardware 2>/dev/null | grep "node 0 cpus" | cut -d: -f2 | tr -d ' ')
    if [ -n "$CPUS" ]; then
        taskset -pc $CPUS$PID 2>/dev/null
    fi

    echo"Done optimizing PID: $PID"
done

六、安全相关参数调优

安全参数也会影响性能,需要在安全和性能之间找平衡。

6.1 网络安全参数

# 开启SYN Cookie防止SYN Flood
sysctl -w net.ipv4.tcp_syncookies=1

# 反向路径过滤(防止IP欺骗)
# 严格模式可能影响多网卡环境
sysctl -w net.ipv4.conf.all.rp_filter=1

# 禁止IP转发(除非是路由器/网关)
sysctl -w net.ipv4.ip_forward=0

# 禁止ICMP重定向接收
sysctl -w net.ipv4.conf.all.accept_redirects=0
sysctl -w net.ipv4.conf.all.secure_redirects=0

# 禁止源路由
sysctl -w net.ipv4.conf.all.accept_source_route=0

# 记录可疑的火星包
sysctl -w net.ipv4.conf.all.log_martians=1

6.2 进程安全参数

# 限制core dump(可能包含敏感信息)
# /etc/security/limits.conf
* hard core 0

# 或者sysctl
sysctl -w fs.suid_dumpable=0

# 限制ptrace(防止进程调试)
sysctl -w kernel.yama.ptrace_scope=2

# 限制dmesg读取
sysctl -w kernel.dmesg_restrict=1

# 限制kernel指针显示
sysctl -w kernel.kptr_restrict=2

6.3 性能与安全的权衡

有些安全特性会带来性能开销:

# Spectre/Meltdown缓解措施会影响性能
# 可以查看当前状态
cat /sys/devices/system/cpu/vulnerabilities/*

# 在完全受控的环境中,可以禁用部分缓解措施(不推荐)
# 启动参数:mitigations=off

# ASLR(地址空间随机化)
cat /proc/sys/kernel/randomize_va_space
# 2是完全随机,生产环境不要改

七、容器环境的特殊考虑

在Docker或Kubernetes环境中,有些参数需要特别注意。

7.1 容器与宿主机共享的参数

容器默认共享宿主机的内核参数,不能在容器内修改以下参数:

  • 网络协议栈参数(除非使用host网络模式)

  • 内存管理参数

  • 文件系统参数

# 需要在宿主机上调优的参数
# /etc/sysctl.d/99-container-host.conf
net.core.somaxconn = 65535
net.ipv4.tcp_max_syn_backlog = 65535
net.ipv4.ip_local_port_range = 1024 65535
fs.file-max = 2097152
fs.inotify.max_user_watches = 524288
vm.max_map_count = 262144

7.2 Kubernetes环境

# kubelet需要的参数
vm.max_map_count = 262144  # Elasticsearch需要
fs.inotify.max_user_watches = 524288  # 文件监控
fs.inotify.max_user_instances = 8192

# 网络参数(取决于网络插件)
net.bridge.bridge-nf-call-iptables = 1
net.bridge.bridge-nf-call-ip6tables = 1
net.ipv4.ip_forward = 1

7.3 Docker特定调优

# Docker daemon配置(/etc/docker/daemon.json)
{
"storage-driver": "overlay2",
"log-driver": "json-file",
"log-opts": {
    "max-size": "100m",
    "max-file": "3"
  },
"default-ulimits": {
    "nofile": {
      "Name": "nofile",
      "Hard": 1048576,
      "Soft": 1048576
    }
  }
}

八、综合调优案例

8.1 案例一:高并发Web服务器

场景:Nginx + PHP-FPM,预期QPS 5万+,连接数10万+

# /etc/sysctl.d/99-webserver.conf

# 网络核心参数
net.core.somaxconn = 65535
net.core.netdev_max_backlog = 65535
net.core.rmem_default = 262144
net.core.rmem_max = 16777216
net.core.wmem_default = 262144
net.core.wmem_max = 16777216

# TCP参数
net.ipv4.tcp_max_syn_backlog = 65535
net.ipv4.tcp_rmem = 4096 87380 16777216
net.ipv4.tcp_wmem = 4096 65536 16777216
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_max_tw_buckets = 262144
net.ipv4.tcp_fin_timeout = 15
net.ipv4.tcp_keepalive_time = 300
net.ipv4.tcp_keepalive_intvl = 30
net.ipv4.tcp_keepalive_probes = 3
net.ipv4.ip_local_port_range = 1024 65535
net.ipv4.tcp_slow_start_after_idle = 0
net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_fastopen = 3

# 拥塞控制
net.core.default_qdisc = fq
net.ipv4.tcp_congestion_control = bbr

# 文件描述符
fs.file-max = 2097152

# 内存
vm.swappiness = 10

Nginx配置配合:

worker_processes auto;
worker_rlimit_nofile 1048576;

events {
    worker_connections 65535;
    use epoll;
    multi_accept on;
}

http {
    # 开启sendfile
    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;

    # keepalive
    keepalive_timeout 65;
    keepalive_requests 10000;

    # 缓冲区
    client_body_buffer_size 128k;
    proxy_buffer_size 128k;
    proxy_buffers 4 256k;

    # 后端keepalive
    upstream backend {
        server 127.0.0.1:9000;
        keepalive 500;
    }
}

8.2 案例二:MySQL数据库服务器

场景:MySQL 8.0,128GB内存,NVMe SSD

# /etc/sysctl.d/99-mysql.conf

# 禁用透明大页
# 需要在启动参数或rc.local中设置
# echo never > /sys/kernel/mm/transparent_hugepage/enabled

# 内存参数
vm.swappiness = 1
vm.dirty_background_ratio = 3
vm.dirty_ratio = 5
vm.dirty_expire_centisecs = 500
vm.dirty_writeback_centisecs = 100

# 配置大页(假设分配80GB给InnoDB Buffer Pool)
# 80GB / 2MB = 40960
vm.nr_hugepages = 40960

# 异步I/O
fs.aio-max-nr = 1048576

# 文件描述符
fs.file-max = 2097152

# 网络参数(连接数较少,主要是连接池)
net.core.somaxconn = 4096
net.ipv4.tcp_max_syn_backlog = 4096

my.cnf配置配合:

[mysqld]
# 使用大页
large-pages

# InnoDB Buffer Pool
innodb_buffer_pool_size = 80G
innodb_buffer_pool_instances = 16

# I/O相关
innodb_io_capacity = 20000
innodb_io_capacity_max = 40000
innodb_flush_method = O_DIRECT
innodb_flush_log_at_trx_commit = 1
innodb_doublewrite = ON

# 连接相关
max_connections = 5000
back_log = 4096

XFS挂载选项:

/dev/nvme0n1p1 /var/lib/mysql xfs noatime,nodiratime,logbufs=8,logbsize=256k,allocsize=16m 0 2

8.3 案例三:Redis缓存服务器

场景:Redis 7.x,32GB内存专用缓存

# /etc/sysctl.d/99-redis.conf

# 内存过量提交(Redis RDB持久化需要)
vm.overcommit_memory = 1

# 禁用透明大页(非常重要!)
# echo never > /sys/kernel/mm/transparent_hugepage/enabled

# 禁用swap
vm.swappiness = 0

# 网络参数
net.core.somaxconn = 65535
net.ipv4.tcp_max_syn_backlog = 65535
net.core.netdev_max_backlog = 65535

# TCP快速回收
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_keepalive_time = 300

Redis配置配合:

# redis.conf
tcp-backlog 65535
maxclients 50000
tcp-keepalive 300

# 关闭RDB自动保存(如果不需要持久化)
save ""

# 或者配置合理的RDB策略
save 900 1
save 300 10
save 60 10000

# AOF配置
appendonly yes
appendfsync everysec

九、故障排查

9.1 网络问题排查

问题1:新连接被拒绝

# 检查SYN队列溢出
netstat -s | grep -i "listen"
# 如果"SYNs to LISTEN sockets dropped"在增长,说明SYN队列满了

# 检查Accept队列溢出
ss -ltn
# 如果Recv-Q接近Send-Q(backlog大小),说明Accept队列满了

# 检查连接数
ss -s
# 解决方案:增大somaxconn和tcp_max_syn_backlog

问题2:TIME_WAIT过多

# 查看TIME_WAIT数量
ss -s | grep TIME-WAIT
netstat -an | grep TIME_WAIT | wc -l

# 查看端口使用情况
ss -tan | awk '{print $4}' | cut -d: -f2 | sort | uniq -c | sort -rn | head

# 解决方案
# 1. 开启tcp_tw_reuse
# 2. 扩大端口范围
# 3. 应用层使用连接池

问题3:网络延迟高

# 检查网卡队列溢出
cat /proc/net/softnet_stat
# 第二列dropped不为0说明有丢包
# 第三列time_squeeze不为0说明CPU忙不过来

# 检查中断分布
cat /proc/interrupts | grep eth

# 解决方案
# 1. 增大netdev_max_backlog
# 2. 调整中断亲和性
# 3. 开启网卡多队列

9.2 内存问题排查

问题1:OOM Killer杀进程

# 查看OOM日志
dmesg | grep -i "out of memory"
journalctl -k | grep -i "oom"

# 查看被杀的进程
dmesg | grep -i "killed process"

# 预防措施
# 1. 调整关键进程的oom_score_adj
# 2. 增加swap
# 3. 设置memory cgroup限制

问题2:swap使用过多导致性能下降

# 查看swap使用情况
free -h
vmstat 1
# 如果si和so列有持续的数值,说明在频繁swap

# 查看哪些进程在使用swap
for file in /proc/*/status; do awk '/VmSwap|Name/{printf $2 " " $3}END{print ""}' $file; done | sort -k 2 -n -r | head

# 解决方案
# 1. 降低swappiness
# 2. 增加物理内存
# 3. 优化应用内存使用

问题3:页面缓存不够用

# 查看页面缓存使用
cat /proc/meminfo | grep -E "Cached|Buffers|MemAvailable"

# 手动释放页面缓存(临时解决)
sync; echo 3 > /proc/sys/vm/drop_caches

# 查看vfs_cache_pressure设置
sysctl vm.vfs_cache_pressure
# 太低会导致dentry和inode缓存占用过多

9.3 I/O问题排查

问题1:I/O等待高

# 使用iostat查看
iostat -xz 1
# 关注await、%util列
# await高说明I/O延迟大
# %util高说明磁盘忙

# 使用iotop查看进程级I/O
iotop -oP

# 解决方案
# 1. 更换调度器
# 2. 优化应用I/O模式
# 3. 升级到SSD

问题2:文件描述符耗尽

# 查看系统级使用情况
cat /proc/sys/fs/file-nr
# 第一列是已分配,第三列是最大

# 查看进程级使用
ls /proc/<pid>/fd | wc -l
# 或者
cat /proc/<pid>/limits | grep "open files"

# 解决方案
# 1. 增大fs.file-max
# 2. 调整limits.conf
# 3. 检查应用是否有fd泄露

9.4 调优后的验证

每次调优后都要验证效果:

#!/bin/bash
# verify_tuning.sh - 验证调优效果

echo"===== Network Parameters ====="
sysctl net.core.somaxconn
sysctl net.ipv4.tcp_max_syn_backlog
sysctl net.ipv4.tcp_congestion_control

echo"===== Memory Parameters ====="
sysctl vm.swappiness
sysctl vm.dirty_ratio
free -h

echo"===== File Limits ====="
sysctl fs.file-max
ulimit -n

echo"===== Current Stats ====="
ss -s
vmstat 1 3
iostat -x 1 3

echo"===== Errors Check ====="
netstat -s | grep -E "overflow|drop|fail"
dmesg | tail -20 | grep -i -E "error|warn|fail"

十、自动化调优脚本

10.1 通用服务器调优脚本

#!/bin/bash
# linux_tuning.sh - Linux内核参数调优脚本
# 作者:SRE团队
# 更新日期:2025-01

set -e

# 颜色输出
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'

log_info() { echo -e "${GREEN}[INFO]${NC} $1"; }
log_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
log_error() { echo -e "${RED}[ERROR]${NC} $1"; }

# 检查是否root用户
check_root() {
    if [ "$(id -u)" != "0" ]; then
        log_error "This script must be run as root"
        exit 1
    fi
}

# 备份当前配置
backup_config() {
    BACKUP_DIR="/root/sysctl_backup_$(date +%Y%m%d_%H%M%S)"
    mkdir -p $BACKUP_DIR
    sysctl -a > $BACKUP_DIR/sysctl_all.txt 2>/dev/null
    cp -r /etc/sysctl.d/ $BACKUP_DIR/ 2>/dev/null || true
    cp /etc/sysctl.conf $BACKUP_DIR/ 2>/dev/null || true
    log_info "Configuration backed up to $BACKUP_DIR"
}

# 获取服务器类型
get_server_type() {
    echo""
    echo"Select server type:"
    echo"1) Web Server (Nginx/Apache)"
    echo"2) Database Server (MySQL/PostgreSQL)"
    echo"3) Cache Server (Redis/Memcached)"
    echo"4) Application Server (Java/Node.js)"
    echo"5) General Purpose"
    read -p "Enter choice [1-5]: " SERVER_TYPE

    case$SERVER_TYPEin
        1) SERVER_TYPE="web" ;;
        2) SERVER_TYPE="database" ;;
        3) SERVER_TYPE="cache" ;;
        4) SERVER_TYPE="application" ;;
        *) SERVER_TYPE="general" ;;
    esac
}

# 基础通用配置
apply_base_config() {
    log_info "Applying base configuration..."

    cat > /etc/sysctl.d/99-base-tuning.conf << 'EOF'
# 文件描述符
fs.file-max = 2097152
fs.inotify.max_user_watches = 524288
fs.inotify.max_user_instances = 8192

# 基础网络参数
net.core.somaxconn = 65535
net.core.netdev_max_backlog = 65535
net.core.rmem_default = 262144
net.core.rmem_max = 16777216
net.core.wmem_default = 262144
net.core.wmem_max = 16777216

# TCP基础参数
net.ipv4.tcp_max_syn_backlog = 65535
net.ipv4.tcp_rmem = 4096 87380 16777216
net.ipv4.tcp_wmem = 4096 65536 16777216
net.ipv4.tcp_timestamps = 1
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_max_tw_buckets = 262144
net.ipv4.ip_local_port_range = 1024 65535
net.ipv4.tcp_fin_timeout = 30
net.ipv4.tcp_syncookies = 1

# BBR拥塞控制
net.core.default_qdisc = fq
net.ipv4.tcp_congestion_control = bbr

# 基础内存参数
vm.swappiness = 10
vm.dirty_background_ratio = 5
vm.dirty_ratio = 10
vm.dirty_expire_centisecs = 1500
EOF
}

# Web服务器配置
apply_web_config() {
    log_info "Applying web server configuration..."

    cat >> /etc/sysctl.d/99-base-tuning.conf << 'EOF'

# Web服务器特定参数
net.ipv4.tcp_slow_start_after_idle = 0
net.ipv4.tcp_fastopen = 3
net.ipv4.tcp_keepalive_time = 600
net.ipv4.tcp_keepalive_intvl = 30
net.ipv4.tcp_keepalive_probes = 3
EOF
}

# 数据库服务器配置
apply_database_config() {
    log_info "Applying database server configuration..."

    cat >> /etc/sysctl.d/99-base-tuning.conf << 'EOF'

# 数据库服务器特定参数
vm.swappiness = 1
vm.dirty_background_ratio = 3
vm.dirty_ratio = 5
vm.dirty_expire_centisecs = 500
vm.dirty_writeback_centisecs = 100
fs.aio-max-nr = 1048576
EOF

    # 禁用透明大页
    if [ -f /sys/kernel/mm/transparent_hugepage/enabled ]; then
        echo never > /sys/kernel/mm/transparent_hugepage/enabled
        echo never > /sys/kernel/mm/transparent_hugepage/defrag
        log_info "Transparent Huge Pages disabled"
    fi
}

# 缓存服务器配置
apply_cache_config() {
    log_info "Applying cache server configuration..."

    cat >> /etc/sysctl.d/99-base-tuning.conf << 'EOF'

# 缓存服务器特定参数
vm.overcommit_memory = 1
vm.swappiness = 0
net.ipv4.tcp_keepalive_time = 300
EOF

    # 禁用透明大页
    if [ -f /sys/kernel/mm/transparent_hugepage/enabled ]; then
        echo never > /sys/kernel/mm/transparent_hugepage/enabled
        echo never > /sys/kernel/mm/transparent_hugepage/defrag
        log_info "Transparent Huge Pages disabled"
    fi
}

# 应用limits.conf
apply_limits() {
    log_info "Applying limits configuration..."

    cat >> /etc/security/limits.conf << 'EOF'

# Added by linux_tuning.sh
* soft nofile 1048576
* hard nofile 1048576
* soft nproc 65535
* hard nproc 65535
root soft nofile 1048576
root hard nofile 1048576
EOF
}

# 主函数
main() {
    check_root

    log_info "Linux Kernel Tuning Script"
    log_info "=========================="

    backup_config
    get_server_type

    apply_base_config

    case$SERVER_TYPEin
        "web")
            apply_web_config
            ;;
        "database")
            apply_database_config
            ;;
        "cache")
            apply_cache_config
            ;;
        "application")
            apply_web_config
            ;;
    esac

    apply_limits

    # 加载BBR模块
    modprobe tcp_bbr 2>/dev/null || log_warn "Failed to load BBR module"

    # 应用配置
    sysctl -p /etc/sysctl.d/99-base-tuning.conf

    log_info "Configuration applied successfully!"
    log_info "Please reboot the system to ensure all changes take effect."
}

main "$@"

10.2 性能监控脚本

#!/bin/bash
# performance_monitor.sh - 性能监控脚本

INTERVAL=${1:-5}
LOG_FILE="/var/log/performance_$(date +%Y%m%d).log"

echo"Starting performance monitoring... (interval: ${INTERVAL}s)"
echo"Logging to: $LOG_FILE"

whiletrue; do
    TIMESTAMP=$(date '+%Y-%m-%d %H:%M:%S')

    # CPU使用率
    CPU_USAGE=$(top -bn1 | grep "Cpu(s)" | awk '{print $2}' | cut -d'%' -f1)

    # 内存使用
    MEM_INFO=$(free -m | grep Mem)
    MEM_TOTAL=$(echo$MEM_INFO | awk '{print $2}')
    MEM_USED=$(echo$MEM_INFO | awk '{print $3}')
    MEM_PERCENT=$((MEM_USED * 100 / MEM_TOTAL))

    # 连接数
    TCP_ESTABLISHED=$(ss -tan | grep ESTAB | wc -l)
    TCP_TIME_WAIT=$(ss -tan | grep TIME-WAIT | wc -l)

    # 磁盘I/O
    IO_AWAIT=$(iostat -x 1 2 | tail -n +7 | head -1 | awk '{print $10}')

    # 输出日志
    echo"$TIMESTAMP | CPU: ${CPU_USAGE}% | MEM: ${MEM_PERCENT}% | ESTAB: $TCP_ESTABLISHED | TW: $TCP_TIME_WAIT | IO_AWAIT: ${IO_AWAIT}ms" | tee -a $LOG_FILE

    sleep $INTERVAL
done
Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐