前言
干了十年运维,见过太多服务器跑着跑着就变成老牛拉破车。明明硬件配置不差,就是性能上不去。很多时候问题不在硬件,而在于Linux内核的默认参数太保守了。
说实话,Linux内核的默认配置是为了通用性设计的,要照顾各种各样的使用场景。但我们生产环境的服务器往往有明确的用途,比如跑Web服务、数据库、消息队列等等。这时候针对性地调整内核参数,性能提升300%真不是吹牛,我在实际项目中做到过好几次。
这篇文章我把这些年积累的内核调优经验整理出来,从网络、内存、I/O、进程调度几个维度系统性地讲一讲。不搞那些虚头巴脑的理论,直接上干货,每个参数都会告诉你为什么要这么调,调完会有什么效果。
一、调优前的准备工作
1.1 了解你的业务场景
在动手之前,先搞清楚你的服务器是干什么的。不同场景的调优方向完全不同:
- 高并发Web服务:重点调网络栈,特别是TCP连接相关参数
- 数据库服务器:重点调内存和I/O,要减少swap,优化页面缓存
- 消息队列:网络和I/O都要调,还要注意文件描述符限制
- 计算密集型服务:重点调CPU调度策略和NUMA配置
我见过不少人拿着网上的调优参数无脑复制,结果越调越差。比如把TCP缓冲区调得巨大,结果内存吃紧触发OOM。所以一定要搞清楚业务特点再动手。
1.2 建立性能基线
调优之前先记录当前的性能数据,不然调完了你都不知道有没有效果。我一般会采集这些指标:
# 记录系统基本信息uname -a > /tmp/baseline/system_info.txtcat /proc/cpuinfo | grep "model name" | head -1 >> /tmp/baseline/system_info.txtfree -h >> /tmp/baseline/system_info.txtdf -h >> /tmp/baseline/system_info.txt# 网络性能基线ss -s > /tmp/baseline/network_baseline.txtcat /proc/net/sockstat >> /tmp/baseline/network_baseline.txt# 内存性能基线vmstat 1 10 > /tmp/baseline/memory_baseline.txtcat /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.somaxconnsysctl vm.swappinesssysctl net.ipv4.tcp_max_syn_backlog
1.4 确认内核版本
不同版本的内核支持的参数不一样,有些新参数在老内核上根本不存在。2025年主流发行版的内核版本:
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=262144sysctl -w net.core.rmem_max=16777216# 所有协议的默认写缓冲区sysctl -w net.core.wmem_default=262144sysctl -w net.core.wmem_max=16777216
这里有个常见误区:不是缓冲区越大越好。缓冲区太大会:
我的经验是:
带宽延迟积的计算公式:
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_reusesysctl -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年主流选择:
- BBR(推荐):Google开发的算法,基于带宽和RTT建模,在丢包网络中表现优异
- CUBIC:Linux默认算法,基于丢包的传统算法,适合低延迟网络
- BBRv2/BBRv3:BBR的改进版本,解决了与CUBIC竞争不公平的问题
启用BBR:
# 加载BBR模块modprobe tcp_bbr# 设置拥塞控制算法为BBRsysctl -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 = 65535net.ipv4.tcp_max_syn_backlog = 65535# TCP缓冲区net.ipv4.tcp_rmem = 4096 87380 16777216net.ipv4.tcp_wmem = 4096 65536 16777216net.core.rmem_default = 262144net.core.rmem_max = 16777216net.core.wmem_default = 262144net.core.wmem_max = 16777216# TCP连接优化net.ipv4.tcp_tw_reuse = 1net.ipv4.tcp_max_tw_buckets = 262144net.ipv4.tcp_timestamps = 1net.ipv4.ip_local_port_range = 1024 65535net.ipv4.tcp_fin_timeout = 30net.ipv4.tcp_slow_start_after_idle = 0# TCP keepalivenet.ipv4.tcp_keepalive_time = 600net.ipv4.tcp_keepalive_intvl = 30net.ipv4.tcp_keepalive_probes = 3# 拥塞控制net.core.default_qdisc = fqnet.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 = 1net.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:
# 完全禁用swapswapoff -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 = 3vm.dirty_ratio = 5vm.dirty_expire_centisecs = 500
这样脏页会更快地刷到磁盘,减少数据丢失风险,但I/O压力会增加。
3.4 大页内存(Huge Pages)
传统的内存页是4KB,对于大内存应用(数据库、JVM)来说,页表会非常大,TLB(Translation Lookaside Buffer)命中率低。
启用大页(2MB或1GB)可以:
# 查看大页信息cat /proc/meminfo | grep Huge# HugePages_Total: 0 总共配置的大页数量# HugePages_Free: 0 空闲大页# HugePages_Rsvd: 0 已预留但未分配的大页# Hugepagesize: 2048 kB 大页大小# 配置大页数量(比如给数据库预留16GB)# 16GB / 2MB = 8192sysctl -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/hugepagesmount -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/enabledecho never > /sys/kernel/mm/transparent_hugepage/defrag
永久禁用需要在启动参数中配置:
# /etc/default/grubGRUB_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 = 5vm.dirty_ratio = 10vm.dirty_expire_centisecs = 1500vm.dirty_writeback_centisecs = 500# 内存过量提交vm.overcommit_memory = 0vm.overcommit_ratio = 50# NUMAvm.zone_reclaim_mode = 0# 大页相关(按需配置)# vm.nr_hugepages = 8192# 内存回收相关vm.min_free_kbytes = 524288vm.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
永久配置:
# /etc/udev/rules.d/60-io-scheduler.rules# NVMe用noneACTION=="add|change", KERNEL=="nvme[0-9]*", ATTR{queue/scheduler}="none"# SATA/SAS用mq-deadlineACTION=="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
关键选项解释:
- 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 1048576root hard nofile 1048576
systemd服务的限制在service文件中配置:
[Service]LimitNOFILE=1048576LimitNPROC=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 = 2097152fs.aio-max-nr = 1048576# inotifyfs.inotify.max_user_instances = 8192fs.inotify.max_user_watches = 524288# 页面缓存相关vm.vfs_cache_pressure = 50vm.dirty_background_ratio = 5vm.dirty_ratio = 10
配合udev规则设置调度器:
# /etc/udev/rules.d/60-io-scheduler.rulesACTION=="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=4000000sysctl -w kernel.sched_min_granularity_ns=500000sysctl -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-3taskset -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# 假设是42echo 0x0f > /proc/irq/42/smp_affinity # 绑定到CPU 0-3# 现代网卡有多队列,每个队列一个IRQ# 可以用irqbalance自动平衡systemctl start irqbalance# 或者手动配置# 禁用irqbalancesystemctl stop irqbalance# 手动均匀分布
对于高性能网络应用,可能需要禁用irqbalance,手动精确控制中断分布。
5.5 cgroup资源限制
使用cgroup可以对进程组进行资源限制和隔离:
# cgroup v2(现代发行版默认)# 创建cgroupmkdir /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# 将进程加入cgroupecho <pid> > /sys/fs/cgroup/myapp/cgroup.procs# 或者在systemd中配置[Service]CPUQuota=50%MemoryMax=2G
5.6 关键进程的调度优化脚本
这是我常用的一个脚本,用于优化关键进程的调度:
#!/bin/bash# optimize_process.sh - 优化关键进程的调度参数PROCESS_NAME=$1if [ -z "$PROCESS_NAME" ]; thenecho"Usage: $0 <process_name>"exit 1fi# 找到进程PIDPIDS=$(pgrep -f "$PROCESS_NAME")if [ -z "$PIDS" ]; thenecho"Process not found: $PROCESS_NAME"exit 1fifor PID in$PIDS; doecho"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/nullfiecho"Done optimizing PID: $PID"done
六、安全相关参数调优
安全参数也会影响性能,需要在安全和性能之间找平衡。
6.1 网络安全参数
# 开启SYN Cookie防止SYN Floodsysctl -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=0sysctl -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# 或者sysctlsysctl -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 容器与宿主机共享的参数
容器默认共享宿主机的内核参数,不能在容器内修改以下参数:
# 需要在宿主机上调优的参数# /etc/sysctl.d/99-container-host.confnet.core.somaxconn = 65535net.ipv4.tcp_max_syn_backlog = 65535net.ipv4.ip_local_port_range = 1024 65535fs.file-max = 2097152fs.inotify.max_user_watches = 524288vm.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 = 1net.bridge.bridge-nf-call-ip6tables = 1net.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 = 65535net.core.netdev_max_backlog = 65535net.core.rmem_default = 262144net.core.rmem_max = 16777216net.core.wmem_default = 262144net.core.wmem_max = 16777216# TCP参数net.ipv4.tcp_max_syn_backlog = 65535net.ipv4.tcp_rmem = 4096 87380 16777216net.ipv4.tcp_wmem = 4096 65536 16777216net.ipv4.tcp_tw_reuse = 1net.ipv4.tcp_max_tw_buckets = 262144net.ipv4.tcp_fin_timeout = 15net.ipv4.tcp_keepalive_time = 300net.ipv4.tcp_keepalive_intvl = 30net.ipv4.tcp_keepalive_probes = 3net.ipv4.ip_local_port_range = 1024 65535net.ipv4.tcp_slow_start_after_idle = 0net.ipv4.tcp_syncookies = 1net.ipv4.tcp_fastopen = 3# 拥塞控制net.core.default_qdisc = fqnet.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 = 1vm.dirty_background_ratio = 3vm.dirty_ratio = 5vm.dirty_expire_centisecs = 500vm.dirty_writeback_centisecs = 100# 配置大页(假设分配80GB给InnoDB Buffer Pool)# 80GB / 2MB = 40960vm.nr_hugepages = 40960# 异步I/Ofs.aio-max-nr = 1048576# 文件描述符fs.file-max = 2097152# 网络参数(连接数较少,主要是连接池)net.core.somaxconn = 4096net.ipv4.tcp_max_syn_backlog = 4096
my.cnf配置配合:
[mysqld]# 使用大页large-pages# InnoDB Buffer Poolinnodb_buffer_pool_size = 80Ginnodb_buffer_pool_instances = 16# I/O相关innodb_io_capacity = 20000innodb_io_capacity_max = 40000innodb_flush_method = O_DIRECTinnodb_flush_log_at_trx_commit = 1innodb_doublewrite = ON# 连接相关max_connections = 5000back_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# 禁用swapvm.swappiness = 0# 网络参数net.core.somaxconn = 65535net.ipv4.tcp_max_syn_backlog = 65535net.core.netdev_max_backlog = 65535# TCP快速回收net.ipv4.tcp_tw_reuse = 1net.ipv4.tcp_keepalive_time = 300
Redis配置配合:
# redis.conftcp-backlog 65535maxclients 50000tcp-keepalive 300# 关闭RDB自动保存(如果不需要持久化)save ""# 或者配置合理的RDB策略save 900 1save 300 10save 60 10000# AOF配置appendonly yesappendfsync 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-WAITnetstat -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 -hvmstat 1# 如果si和so列有持续的数值,说明在频繁swap# 查看哪些进程在使用swapfor 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/Oiotop -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.somaxconnsysctl net.ipv4.tcp_max_syn_backlogsysctl net.ipv4.tcp_congestion_controlecho"===== Memory Parameters ====="sysctl vm.swappinesssysctl vm.dirty_ratiofree -hecho"===== File Limits ====="sysctl fs.file-maxulimit -necho"===== Current Stats ====="ss -svmstat 1 3iostat -x 1 3echo"===== 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-01set -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 1fi}# 备份当前配置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_TYPEcase$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 = 2097152fs.inotify.max_user_watches = 524288fs.inotify.max_user_instances = 8192# 基础网络参数net.core.somaxconn = 65535net.core.netdev_max_backlog = 65535net.core.rmem_default = 262144net.core.rmem_max = 16777216net.core.wmem_default = 262144net.core.wmem_max = 16777216# TCP基础参数net.ipv4.tcp_max_syn_backlog = 65535net.ipv4.tcp_rmem = 4096 87380 16777216net.ipv4.tcp_wmem = 4096 65536 16777216net.ipv4.tcp_timestamps = 1net.ipv4.tcp_tw_reuse = 1net.ipv4.tcp_max_tw_buckets = 262144net.ipv4.ip_local_port_range = 1024 65535net.ipv4.tcp_fin_timeout = 30net.ipv4.tcp_syncookies = 1# BBR拥塞控制net.core.default_qdisc = fqnet.ipv4.tcp_congestion_control = bbr# 基础内存参数vm.swappiness = 10vm.dirty_background_ratio = 5vm.dirty_ratio = 10vm.dirty_expire_centisecs = 1500EOF}# 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 = 0net.ipv4.tcp_fastopen = 3net.ipv4.tcp_keepalive_time = 600net.ipv4.tcp_keepalive_intvl = 30net.ipv4.tcp_keepalive_probes = 3EOF}# 数据库服务器配置apply_database_config() { log_info "Applying database server configuration..." cat >> /etc/sysctl.d/99-base-tuning.conf << 'EOF'# 数据库服务器特定参数vm.swappiness = 1vm.dirty_background_ratio = 3vm.dirty_ratio = 5vm.dirty_expire_centisecs = 500vm.dirty_writeback_centisecs = 100fs.aio-max-nr = 1048576EOF# 禁用透明大页if [ -f /sys/kernel/mm/transparent_hugepage/enabled ]; thenecho never > /sys/kernel/mm/transparent_hugepage/enabledecho 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 = 1vm.swappiness = 0net.ipv4.tcp_keepalive_time = 300EOF# 禁用透明大页if [ -f /sys/kernel/mm/transparent_hugepage/enabled ]; thenecho never > /sys/kernel/mm/transparent_hugepage/enabledecho never > /sys/kernel/mm/transparent_hugepage/defrag log_info "Transparent Huge Pages disabled"fi}# 应用limits.confapply_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 65535root soft nofile 1048576root hard nofile 1048576EOF}# 主函数main() { check_root log_info "Linux Kernel Tuning Script" log_info "==========================" backup_config get_server_type apply_base_configcase$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 $INTERVALdone
十一、调优检查清单
完成调优后,用这个清单逐项检查:
网络层面
- [ ] tcp_max_syn_backlog >= 65535
- [ ] TCP Fast Open已启用(如需要)
内存层面
- [ ] swappiness设置合理(10或更低)
I/O层面
进程层面
监控验证
十二、总结
Linux内核调优是一门实践性很强的技术,没有放之四海而皆准的参数配置。核心原则是:
这些年调优的经验告诉我,性能问题80%在应用层,15%在配置层,只有5%需要内核调优。如果服务器性能不行,先从应用代码和架构设计找问题,实在找不到再来调内核参数。
内核调优能带来性能提升,但期望值要合理。说提升300%,那是在默认参数非常不适合业务场景的前提下。如果默认配置本来就能满足需求,硬调反而可能带来问题。
最后,生产环境的任何改动都要谨慎。先在测试环境验证,再灰度发布,最后全量推广。出问题能快速回滚才是运维的基本素养。
希望这篇文章对你有帮助。如果你有什么问题或者不同的经验,欢迎交流讨论。