简介:围绕 Linux 性能调优整理的文档资料,主要面向需要优化 Red Hat Enterprise Linux AS 与 SUSE LINUX Enterprise Server 运行效率的系统管理员和运维工程师。内容按关闭 daemons、关闭 GUI、改变内核参数、处理器子系统调优、内存子系统调优、文件系统调优、网络子系统调优等模块展开,每部分均结合具体命令说明操作方法。例如用 service 或 init.d 停用后台服务,用 chkconfig 关闭自启动;用 runlevel 与 init 切换运行级别;修改 /etc/sysctl.conf 或 /proc/sys/vm 下的参数调整内存与网络行为;用 taskset 设置 CPU 亲和性,用 nice、renice 管理进程优先级;调整文件系统挂载选项和磁盘 I/O 调度器。资源为单份 docx 文档,压缩包约 373KB,方便在办公软件中阅读、检索和打印;目前已有 238 人学习下载。文档同时提醒关闭 xfs daemon 可能影响 X 启动,强调调整参数后应做基准测试,避免过度优化,能为排查性能瓶颈提供较为完整的参考路径。
1. Linux性能调优从哪里下手:先分清瓶颈再动手
我处理过不少"服务器卡顿"的工单,典型画面是业务方说接口超时,我上去 top 一看,CPU 利用率只有 12%,load average 却已经飙到 18,进程列表里挤着一串 D 状态。很多人遇到这种情况第一反应是加 CPU,但 Linux 性能调优的核心从来不是把单个参数调到极限,而是先用指标把瓶颈定位到 CPU、内存、磁盘、网络这四个维度之一,再针对性地动手。下面按四条最常见的调优路径展开,从 linux 常用命令怎么读,到参数怎么改、改完怎么验证,最后把我踩过的几个坑一并列出来。适合运维、后端开发以及做嵌入式 linux 系统的人照着排查。
2. CPU与进程调度调优:从top看负载到taskset绑核
2.1 先读懂CPU指标:load average、us/sy/wa/id
top 第一行的 load average 三个数字分别代表 1、5、15 分钟的平均负载。很多人把它当成 CPU 利用率,其实它统计的是 R 状态(正在运行)和 D 状态(不可中断睡眠,通常是等待 IO)进程数的总和。反过来,CPU 利用率低不代表系统不忙,负载高而 CPU 空闲时,优先怀疑 D 状态进程在排队等磁盘或网络。
top 进入后按数字 1 键,能看到每个逻辑核独立的 us(用户态)、sy(内核态)、wa(IO 等待)、id(空闲)四列。我一般按下面三条来判断:wa 高,先查磁盘 IO,不是 CPU 问题;sy 高,重点看系统调用、锁竞争、中断处理,别急着加核;us 逼近 100%,才算真正的算力瓶颈,这时候才轮到 CPU 调优。
配合 vmstat 看运行队列更直观。r 列是正在运行和等待 CPU 的进程数,b 列是阻塞在 IO 上的进程数。r 长期大于逻辑核数说明 CPU 排队,b 长期非零说明 IO 有问题。连续观察三次再下结论,单次 top 快照经常被瞬时尖刺干扰。
# 2秒刷新一次,进入后按1展开每个逻辑核 top -d 2 # 每秒采样一次,共5次,重点看 r、b、us、sy、wa 五列 vmstat 1 5top 的 -d 2 表示刷新间隔为 2 秒,连续观察比单次快照更能看出趋势;vmstat 1 5 是每 1 秒采样一次、共输出 5 组数据。r 列的数值直接对应等待调度的任务数,如果 r 长期是核数的好几倍,说明用户态进程已经抢不到 CPU 时间片。如果所有系统指标正常但业务仍然卡顿,再用 perf stat 或 strace 看应用内热点,那已经属于应用层调优的范畴。
什么时候才值得做 CPU 调优?我的判断标准是:us 稳定在 85% 以上、r 队列长期超过核数的 4 倍,同时 vmstat 的 cs(上下文切换)列每秒几万次以上。这种组合说明进程在频繁切换中空转,优先级和绑核才有明显收益。否则就算把 nice 调到底,瓶颈还在别的维度,属于白费劲。
2.2 用nice/renice调整优先级:命令行与systemd配置
定位到 CPU 瓶颈后,第一个能做的调优是调整进程优先级。运行队列里所有进程按优先级排队,nice 值范围是 -20 到 19,数值越小优先级越高,普通用户只能调大(降级),调小需要 root。业务上最常见的用法,是把备份、日志压缩、数据迁移这类"慢就慢点"的任务降优先级,把数据库和网关进程的优先级适当提高,这在实际的 mysql 性能调优场景里很常见。
# 启动备份脚本并降低优先级 nice -n 10 ./backup.sh & # 调整已经在跑的进程,-5 表示提高优先级,必须 root 执行 renice -n -5 -p $(pidof mysqld)nice -n 10 表示把进程的 CPU 调度优先级降到 10,适合后台批量任务;renice 则作用于运行中的进程,$(pidof mysqld) 动态取进程号,避免手动查 PID。注意一点:不要为了"更快"把所有核心进程都调成负 nice,优先级过高会导致系统响应异常,极端情况下连 ssh 都连不上,属于典型的翻车姿势。
如果进程由 systemd 托管,直接在单元文件里加调度参数更稳妥。在 [Service] 段下写 Nice=10、CPUSchedulingPolicy=rr,再执行 systemctl daemon-reload 并重启服务,效果和命令行一致,而且重启后不会丢。云主机上还要注意 cgroup 的 cpu.weight 会在更高层级限流,外层限制没放开时,内核 nice 调整的效果会被削掉一截。
调整完怎么确认生效?用 ps 带格式参数看,命令是 ps -o pid,pri,ni,comm -p 进程号,ni 列变成目标值就说明生效。这里有个前提:如果进程本身卡在 IO 等待上,调高优先级不会让磁盘变快,perf 记录里会看到处理器大部分时间在等待事件,这种场景改 nice 没有任何意义。
2.3 绑核与隔离:taskset与isolcpus
优先级只能解决竞争,解决不了多个高负载进程互相抢占同一个核的问题。绑核是把进程固定到指定 CPU 上,减少上下文切换和缓存抖动。对数据库实例、转发网关这类延迟敏感的服务,我一般会单独留两个核给它。嵌入式 linux 设备上核数少,绑核通常比调 nice 更直接。
# 把 nginx 的 master 进程绑到 2、3 号核 taskset -c 2,3 nginx # 查某个进程当前绑在哪些核上 taskset -cp $(pidof nginx) # 内核启动参数做核隔离,改 /etc/default/grub 中的 GRUB_CMDLINE_LINUX # 追加 isolcpus=4,5 后执行 grub2-mkconfig -o /boot/grub2/grub.cfg 并重启taskset -c 2,3 是绑核命令,表示进程只允许在 2 号和 3 号核上运行;isolcpus=4,5 是内核启动参数,把 4、5 号核从普通调度中隔离出来,普通用户态进程默认不会跑上去,配合 taskset 手动放进去的服务使用。需要注意:隔离的核要留够,别把系统全隔离了;另外在云主机上,宿主机看不到真实拓扑,isolcpus 可能没效果,物理机或嵌入式场景更实用。
注意:isolcpus 隔离的核仍然会处理内核中断,需要配合 irqbalance 或手动把中断亲和性写到其他核,才能真正做到"专用核"。
绑核效果怎么验证?用 perf stat -e context-switches,cpu-migrations sleep 10 观察 cpu-migrations 计数,如果接近 0,说明进程已经不再在核之间迁移。这里的逻辑是:进程不迁移意味着缓存更热、上下文切换更少,但代价是单核负载集中,所以要给绑定的核留足余量。
3. 内存与Swap调优:free命令背后的几个关键参数
3.1 先看懂free输出:buffer/cache不等于占用
内存调优的第一步,是确认内存到底够不够。free -m 的输出里,used 列看着很高,大部分其实是 page cache(文件缓存)。Linux 会把空闲内存尽量拿去做缓存,业务需要时再回收,所以判断依据不是 used 而是 available 列,它才是"还能分给新进程的真实内存"。
# 以MB为单位查看内存 free -m # 更精确地看内核 commit 了多少内存 grep -i commit /proc/meminfofree -m 里的 available 来自内核估算,比直接算 used+free 可靠得多。当 available 持续低于总内存的 10%,说明内存确实吃紧。CommitLimit 和 Committed_AS 两行表示内核承诺给进程的内存上限和当前已承诺量,配合超卖场景判断是否会发生 OOM 很有用。另外观察 page cache 的回收速度也有参考价值:如果 available 下降很快但 cache 一直在开大,说明有人在大量读文件,磁盘 IO 可能才是第一瓶颈。
3.2 swappiness怎么调:换页倾向不是越大越好
swap 是内存不足时的兜底,但换页本身损耗很大。vm.swappiness 取值范围 0 到 100,默认 60,数值越大越倾向于把不常用的内存页换到 swap,越小越倾向于回收 page cache。对交互式应用或数据库,我一般降到 10 左右;如果机器磁盘是机械盘,宁可让 OOM 去杀进程,也不要让整机陷入换页泥潭,这是血泪经验。
# 临时调整,重启失效 sysctl -w vm.swappiness=10 # 永久生效,写入 /etc/sysctl.d/99-perf.conf # vm.swappiness=10 sysctl --systemsysctl -w 是临时写运行时参数,适合先验证效果;sysctl --system 会按 /etc/sysctl.d/ 下所有 conf 文件依次加载,比直接改 /etc/sysctl.conf 更好维护。这里的关键是理解 swappiness 不是"内存用了多少才换页"的阈值,而是内核回收内存时对换页的"倾向权重"。调完以后用 swapon --show 看 swap 使用率,如果 swap 长时间不增长,说明这个值对你的负载是合适的。
3.3 overcommit 三档含义与OOM防护
vm.overcommit_memory 控制内核允许超量分配内存的程度。0 是启发式,大多数情况够用,但碰到申请一大块内存再逐步使用的程序会误判;1 是允许任意 overcommit,适合内存池类应用,但系统很容易在真正需要内存时找不回来;2 是禁止超过 CommitLimit 的分配,最保守,配合 overcommit_ratio 能防止单个大内存进程把系统拖死。
# 使用保守模式,承诺比例设为物理内存的80% sysctl -w vm.overcommit_memory=2 sysctl -w vm.overcommit_ratio=80overcommit_ratio 只有在模式 2 下生效,表示允许承诺的内存是"物理内存加 swap 的多少倍",填 80 就是 80%。数据库服务我倾向模式 2,因为它能把失控进程挡在 CommitLimit 之外,宁可让它分配失败,也不要整机死锁。模式 1 适合明确知道自己内存池大小、且能容忍小概率 OOM 的中间件。一旦发生 OOM,第一时间看 /var/log/messages 或 journalctl -k,里面会记录内核选了哪个进程、当时的内存水位,这是判断参数是否过严的最快路径。
3.4 参数持久化:别让重启丢掉所有改动
整套内存参数改完,最怕重启后全部失效。规范化做法是在 /etc/sysctl.d/ 下建独立文件,每个参数一行注释,谁改的、为什么改都写清楚。系统启动时 systemd-sysctl 服务会自动加载这个目录,不需要手工执行。
# 新建文件,写入并验证 cat > /etc/sysctl.d/99-perf.conf <<'EOF' # 降低换页倾向,减少磁盘抖动 vm.swappiness=10 # 保守overcommit,保护数据库进程 vm.overcommit_memory=2 vm.overcommit_ratio=80 EOF sysctl --system sysctl vm.swappinesscat 加 EOF 直接生成配置文件,避免 vim 误操作;sysctl --system 重新加载全部文件,最后一条 sysctl vm.swappiness 单独确认当前生效值。注意:同一个参数出现在多个文件时,文件名排序靠后的会覆盖靠前的,所以 99- 这个前缀是我个人习惯,保证覆盖系统默认配置。还有一类特殊参数是只读的,写入会报 kernel parameter 错误,这种参数只能通过 grub 命令行传,别在 sysctl.d 里浪费时间。
4. 磁盘IO与网络栈调优:iostat、调度器与网卡队列
4.1 iostat指标解读:await和util的组合判断
磁盘饱和的特征不是 CPU 变高,而是 wa 升高、进程进入 D 状态。iostat -x 能给出每个磁盘的详细指标,最常用的判断组合是 util 和 await。util 接近 100% 说明磁盘几乎一直在忙,但这不能直接等于性能差,还要看 await 是否超过磁盘能承受的延迟水平。
# 每2秒采样,连续5次,显示扩展统计 iostat -x 2 5-r 和 -w 可以拆开看读写分离,但日常我只看几个字段:util 是设备忙闲占比,await 是 IO 请求从进队到完成的总等待时间,r_await/w_await 是读写各自的平均等待。如果 await 高而 util 低,大概率是 IO 请求本身排队严重,或者磁盘降速了;util 高且 await 接近物理极限,才是真的需要换盘或做读写分离。机械盘的合理随机 IO 延迟在 10ms 上下,SSD 在 1ms 以内,超过这个量级先怀疑调度和队列深度,再怀疑硬件。
4.2 磁盘调度器:机械盘用mq-deadline,NVMe用none
Linux 内核用调度器决定 IO 请求的排队顺序。CFQ 在旧内核里是默认值,新内核改成多队列模式后,常见选项是 mq-deadline 和 none。机械盘多并发场景用 mq-deadline 能尽量合并相邻请求,SSD 和 NVMe 因为寻道时间接近 0,直接选 none 减少一层排队开销。
# 查看当前调度器 cat /sys/block/sda/queue/scheduler # 临时切换 echo mq-deadline > /sys/block/sda/queue/scheduler # 永久生效:在 /etc/udev/rules.d/ 下按磁盘类型设置scheduler 文件里方括号标着当前使用的调度器,echo 写入即可临时切换。永久生效常见做法是写 udev 规则,按盘符或型号匹配后设置;也可以在 grub 内核参数里加 elevator=mq-deadline 统一指定。要注意的是云主机的虚拟磁盘有些只支持 none,写入别的会直接报错,改之前先 cat 看清楚支持哪些。
提示:调度器改动后立即生效,但重启会还原;线上环境务必写成 udev 规则或 grub 参数,不要每次开机手动 echo。
4.3 网络栈调优:ss看队列,网卡队列与TCP缓冲
网络调优的第一步还是先看指标。ss -lnt 输出的 Send-Q 和 Recv-Q 能反映 socket 队列堆积情况。Recv-Q 长期不为 0 说明应用来不及读数据,Send-Q 长期不为 0 说明对端处理慢或网络拥塞。这两个队列溢出时,抓包是看不到明显丢包的,因为它们还没走到协议栈的丢包逻辑。
# 查看监听端口和队列 ss -lnt # 查看网卡支持的中断队列数 ethtool -l eth0网卡多队列是老生常谈的调优点。ethtool -l 看 Combined 值,如果网卡支持 8 条队列而系统只用 1 条,可以把 Combined 调高,配合 RPS/RSS 把中断分散到多个核,多核机器上能明显改善单核 softirq 打满的问题。RPS 的手动配置也很简单,把 rps_cpus 写成目标核的 CPU 位图即可,比如 4 核机器想用后 3 个核就写 0xe。
# 积压队列相关参数,常规修改项 sysctl -w net.core.somaxconn=1024 sysctl -w net.ipv4.tcp_max_syn_backlog=2048somaxconn 是 listen 队列上限,tcp_max_syn_backlog 是半连接队列上限。这两个参数只在应用 backlog 配得足够大时才生效,如果 nginx 的 backlog=512,内核 somaxconn 调到 65535 也没意义。网络调优的原则是"参数配合应用一起看",孤立的 sysctl 只会制造假优化。遇到 TCP 重传先确认丢包发生在哪个方向,调大缓冲前先排除对端处理能力和链路问题。
4.4 文件系统挂载参数:noatime与日志模式
文件系统层面的调优容易被忽略,但收益很直接。mount 默认记录每次文件访问时间(atime),高频读场景下这个写操作会放大 IO 次数。noatime 挂载参数可以去掉这个行为。数据库数据盘我会顺手把日志模式调整一下,ext4 的 data=ordered 比 data=writeback 更谨慎,但后者写入时延更低,适合能接受异常断电丢最近数据的场景。
# 查看当前挂载参数 mount | grep /data # 临时重挂载,不卸载磁盘 mount -o remount,noatime /dataremount 不需要卸载就能改挂载参数,生产环境也能执行。注意:noatime 只影响读操作的时间戳更新,对写密集型应用没有优化作用;如果应用依赖 atime 做失效判断(比如某些邮件系统),开了 noatime 反而会引出新问题,这类功能取舍必须确认应用行为后再做。
5. Linux性能调优避坑:改错参数比不改更糟
5.1 swappiness设成100,Swap抖动反而拖慢数据库
现象:业务反馈接口延迟飙升,free 输出显示 swap 使用率在 5% 和 30% 之间反复跳动,CPU softirq 增高,磁盘 IO 里能看到大量换页写。
原因:把 vm.swappiness 设置成了 100,内核回收内存时极度偏好换页,大量冷热不分的内存页被反复写入磁盘,机械盘和普通 SSD 根本扛不住换页流量。
解决:改回 10 左右,等 swap 使用率回落后再观察。如果内存真的不够,正确做法是加内存或限制缓存,而不是靠调 swappiness 硬撑。这个参数的调优收益有上限,对数据库这类重负载进程,换页带来的抖动远大于回收 page cache 的收益。
5.2 overcommit_memory=1 后 MySQL 分配内存失败
现象:设置 vm.overcommit_memory=1 后,MySQL 实例反复报内存分配失败,日志里出现 Out of memory 且进程被 OOM Killer 选中。
原因:模式 1 允许内核无限超量承诺,应用申请大块内存时总能成功,但真正访问内存页时物理内存不够,内核只能请 OOM Killer 找进程下手。MySQL 这类内存大户申请了不用,用了才触发缺页,最容易被选中。
解决:改用模式 2 并设置合理的 overcommit_ratio,让内核在承诺阶段就拦住超量请求。我的经验是数据库机器 overcommit_ratio 设在 80 起步,再按实际占用逐步微调,同时用 systemd 的 MemoryMax 从 cgroup 层面再兜一层。
5.3 机械盘换 none 调度器,延迟不降反升
现象:按 SSD 调优教程给老机械盘执行 echo none > /sys/block/sda/queue/scheduler,结果随机读写延迟从 20ms 涨到 60ms,IO 吞吐反而下滑。
原因:机械盘寻道是物理瓶颈,none 调度器不做请求合并和排序,随机 IO 全部直接打给磁盘,磁头来回跑,延迟自然变差。mq-deadline 和 bfq 存在的意义就是给机械盘做电梯算法。
解决:换回 mq-deadline。教训是调度器选型先看介质:机械盘选 deadline,SSD 选 none,别拿一套配置套所有机器。
5.4 sysctl 改了当时生效,重启全部还原
现象:做完内存调优后 sysctl -w 验证一切正常,过几天重启后 free 输出又回到原来的行为。
原因:sysctl -w 修改的是运行时内核参数,不会写入配置文件。没有在 /etc/sysctl.d/ 下建立持久化文件,重启后自然恢复默认。
解决:写完临时值验证没问题后,立刻同步写入 /etc/sysctl.d/99-perf.conf,再用 sysctl --system 重新加载。我的习惯是每改完一个参数,马上加一行注释写入文件,避免"今天临时试一下"变成"重启后踩坑"。
5.5 一次改十个参数,出了问题不知道回滚谁
现象:同时调整了 swappiness、overcommit、TCP 缓冲区、IO 调度器,线上出现性能回退,排查时每个参数看起来都是可疑的。
原因:多个参数之间存在耦合,比如调大 TCP 缓冲区会占用更多内存,进而触发更激进的内存回收,单参数验证时看不到这个连锁反应。
解决:每次只改一个参数,记录 baseline 指标,验证有效再继续下一个。需要回退时,优先回退最近一次改动,而不是把所有配置推倒重来。
6. 调优效果的闭环验证:sysbench、fio 与 perf 怎么配合
调优做完不验证等于白做。我习惯在执行任何改动前先记一组 baseline,改动后复测同一组命令,用数字对比代替"感觉快了"。以一次 MySQL 主机优化为例,我按下面的顺序把四个维度各测一遍,结果汇总成表:
| 指标 | 调优前 | 调优后 | 变化 |
|---|---|---|---|
| 平均负载 load1 | 8.2 | 3.5 | -57% |
| 磁盘 await(ms) | 36 | 21 | -42% |
| swap 使用率 | 85% | 12% | -73% |
| 接口 P99 延迟(ms) | 920 | 410 | -55% |
CPU 和内存用 sysbench:sysbench cpu run 测整数性能,sysbench memory run 测内存带宽,跑完看 events per second 和吞吐量。磁盘用 fio 更贴近真实负载,下面这组命令用 libaio 引擎直写绕过 page cache,测得的是纯粹的磁盘性能。
# 调优前先跑一次,记录数值 fio --name=test --rw=randread --bs=4k --size=1G \ --ioengine=libaio --direct=1 --iodepth=32 \ --numjobs=4 --runtime=30 --group_reporting # 调优后再跑一次,对比 IOPS 和 lat avg(usec)--direct=1 绕过 page cache,确保测的是磁盘而非文件缓存;--runtime=30 控制测试时长,避免把磁盘写满。对比时重点看 read IOPS 和平均延迟两个数值,变化幅度超过 10% 才值得关注,小幅度波动多半是噪声。网络验证用 iperf3 打满带宽测吞吐,记录重传率。最后再用 perf top 看一眼热点,确认调优没有引入新的异常系统调用。
说句实话,性能调优里最容易翻车的不是参数难记,而是贪心。我自己的习惯是:一次只动一个参数,改完立刻记录结果,确认有效再碰下一个,这个习惯帮我少踩了很多坑。希望帮到你。
本文还有配套的精品资源,点击获取