ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Linux性能调优实战:从瓶颈定位到参数固化

Linux性能调优实战:从瓶颈定位到参数固化 简介面向Linux运维与系统管理员资源以Red Hat Enterprise Linux AS和SUSE LINUX Enterprise Server为对象完整梳理企业级环境下的性能调优方法。文档从关闭多余daemons、停用GUI以释放内存与CPU切入给出Red Hat与SUSE各自的service、chkconfig、YaST2操作命令再介绍通过sysctl和/etc/sysctl.conf调整内核参数覆盖网络、内存管理与I/O调度。后续按子系统展开处理器亲和性与优先级控制、虚拟内存脏页写回、文件系统类型及挂载选项、TCP/IP栈参数并补充了切换运行级别、减少虚拟控制台、使用powertweak图形化修改等技巧。内容兼顾基础命令与进阶配置帮助读者针对实际负载做安全调优、进行基准测试并避免过度优化。资源为1个docx文档大小约373KB结构清晰、条目分明适合初中级运维人员在部署调优时查阅。已有238人学习浏览是一份实用的服务器性能优化参考。1. Linux 性能调优先判断要不要动参数再决定动哪一个生产服务器 CPU 冲到 95%你打开终端敲了top看到几个进程在抢却不敢动参数。网上那类 Linux 性能调优方法总结里翻来覆去就是vm.swappiness、dirty_ratio、noatime这些关键词可哪一个值得在这个现场动手没有一个人敢打包票。我经手过的性能问题里一半以上不是缺参数而是开头就把瓶颈判断错了。Linux 性能调优的难点不在命令冷僻而在能不能用mpstat、vmstat、perf在一分钟内读出系统的真实承担比例。顺序对了通常只需要改两三个参数顺序反了参数调成玄学也优化不了一点点。这篇文章按“先定位、再调参、最后验证”走把 CPU、内存、IO 三类手段各给一套能复现的命令再单独说几个容易翻车的组合。适合刚接手 Linux 服务器的人也适合带生产系统、又不想被内核参数反向教育的系统管理老手。2. 先定位瓶颈再动参数用 mpstat、vmstat、pidstat、perf 读出现状“系统卡”这个现象本身没有太多信息量MySQL 慢查询、Nginx 高延迟、日志压缩任务卡住原因链条完全不一样。网上的调优清单经常互相打架多半是因为读者手里的瓶颈类型不同。我自己的原则是先采集再决定采集不是free -m和uptime两样东西而是一分钟内能体现差异的数据%iowait是持续在 30% 以上还是偶尔跳 3%si/so是不是每轮采样都不为 0。只有拿到这类数据后续调整才有依据。这一章的命令很多人会当成 Linux 常用命令大全来背。我建议反过来先知道每条命令回答什么问题再考虑记不记得住参数。下面三个小节就是按“整机 CPU、内存与 IO 区分、进程与函数定位”的顺序来的。2.1 用 mpstat 判断 CPU 是真满载还是假满载mpstat -P ALL 1 5-P ALL让每个逻辑核单独输出一行1 5表示每秒采样一次一共输出 5 次。重点看%usr、%sys、%iowait、%soft、%steal这几列。%usr高而%sys低说明消耗在业务进程的计算上%sys高说明应用在内核态折腾比如频繁系统调用、锁竞争、中断集中。%iowait高表示 CPU 在等磁盘或网络 IO 结束这个时候加 CPU 是白花钱。%steal出现在虚拟机和云主机上宿主机抢占 CPU 时间突然飙高要先查宿主机邻居是不是在满负载。实际看数据时有个容易踩的细节%idle大不代表没问题。在-P ALL输出里如果只有一个核忙到 100%、其余核全空闲整机%idle依然高于 90%但这是典型的单点热点。生产上常见日志采集进程用单线程又绑定到某个核现象就是某个核%usr100%整机负载看起来不高业务还是被它拖慢。不要把整机平均值当唯一判断标准。2.2 用 vmstat 区分三种压力CPU 饱和、内存回收、IO 等待vmstat 1 10输出字段按顺序排开r是正在运行或等待运行的线程数b是阻塞在 IO 等待上的进程数swpd是交换空间用量si/so是每秒换入换出的页数us/sy/id/wa是 CPU 时间切分。把判断顺序固定成三步避免乱扣结论。先看si和so。如果这两个值连续几次采样都不为 0说明物理内存装不下工作集系统正在匿名页和交换空间之间反复搬数据。这时候 CPU 调优、IO 调度器优化都要往后放先加内存或降内存占用。注意vmstat的free列是未使用的物理内存page cache 被记在cache列里所以free小、cache大不一定内存不够内核在压力下可以回收 page cache。再看b和wa。b大、wa高IO 是瓶颈去查磁盘队列深度和延迟。wa表示 CPU 等待 IO 结束的空转时间不直接代表磁盘忙不忙所以哪怕wa只有 10%、b很小也可能是 IO 次数多而单次延迟低。最后才看ussy这列高才算真正 CPU 满载。按这个顺序判断能少走很多弯路。2.3 用 pidstat 和 perf 把问题从系统钉到进程再钉到函数pidstat -p ALL 1 5pidstat列出每个进程的 CPU 使用率、内存增量等比top手动翻页更适合事后复盘数据是按进程拍平的时间序列。看到某个进程长时间占满 CPU再往下追一步它到底在哪个函数上烧 CPU。perf topperf top在当前终端实时刷新按 CPU 指令热点排列进程和函数。想留档对比可以分两条跑perf record -g -p pid -o /tmp/perf.data -- sleep 30 perf report -i /tmp/perf.data --stdioperf record的-g是采集调用栈-p指定进程-- sleep 30表示采集 30 秒后自动结束perf report把采样结果按占比排出来。Java、Node 这类带 JIT 的运行时需要额外加符号表通常还要配合语言自身的 profiling 工具但系统层面定位到这里已经足够支撑下一步选择调优手段。3. CPU 调优的落地手段nice、taskset、isolcpus 与调度参数CPU 类调优在 Linux 面试题里出现频率很高常问的也就是优先级、亲和性、内核调度参数。实际做的时候顺序是先看要不要调整进程调度优先级再看要不要绑核最后才考虑隔离核和修改内核调度行为。3.1 临时调整优先级和绑定核renice、taskset同一个数据库实例上备份任务半夜跑、日志清理也在半夜跑两个任务抢 CPU 导致备份时间比平时多一倍。最简单的做法是给备份任务降优先级让数据库进程保持高优先级# 降低备份进程的调度优先级 renice -n 10 -p $(pgrep -f backup.sh) # 把某个进程绑定到 CPU2 和 CPU3 taskset -pc 2,3 $PID # 启动新进程时直接指定 CPU 亲和性 taskset -c 0,1 /usr/bin/myapp --config /etc/myapp.confrenice只改 CPU 调度优先级不改内存和 IO 优先级范围是 -20 到 19数值越小越优先。数据库这类核心进程通常保持 0 就好没必要调成负数备份、编译任务可以放到 10 以上避免影响主业务。taskset设的是亲和性把进程钉在指定的逻辑核上。要注意在多线程进程上建议加-a让所有线程一起生效只绑主线程的话其它线程照旧乱跑。还有一个容易被忽略的点在 NUMA 机器上taskset绑定的 CPU 和内存访问位置要配合。如果进程固定到 CPU0但内存散落在远端节点访问延迟反而上涨。确认 NUMA 拓扑用lscpu或numactl --hardware后面避坑章还会展开。3.2 用 isolcpus 和 cpuset 隔离出专用 CPU给延迟敏感任务留一间空房把部分物理核从默认调度器中剔除普通进程默认不会被调度到这些核上再让延迟敏感服务绑定过去可以有效避开上下文切换噪声。修改方式在引导参数里# /etc/default/grub GRUB_CMDLINE_LINUXisolcpus2,3 nohz_full2,3 rcu_nocbs2,3 sudo update-grub sudo rebootisolcpus2,3表示把 CPU2、CPU3 从通用调度器中拿出来nohz_full2,3让这两个核尽量不接收周期 tick减少定时器打断rcu_nocbs2,3不让 RCU 回调落在这些核上。重启后通过cat /proc/cmdline确认参数生效。隔离出来的核不是越多越好。四个核里隔两个剩下两个核要扛所有普通进程整机吞吐可能不升反降。更灵活的做法是用 cgroup v2 的 cpusetmkdir /sys/fs/cgroup/rt-cpus echo 2-3 /sys/fs/cgroup/rt-cpus/cpuset.cpus echo 0 /sys/fs/cgroup/rt-cpus/cpuset.mems echo $PID /sys/fs/cgroup/rt-cpus/cgroup.procscpuset.mems指定允许使用的内存节点在 NUMA 机器上必须设置写 0 代表只能从 node0 分配内存。cgroup 方式的好处是不用重启可以按业务进程动态划分适合容器场景。3.3 中断均衡与 CPU 频率irqbalance、sched_autogroup、cpupower硬件中断集中在一个核上时%sys会偏高这时候 irqbalance 服务能帮上忙。它把中断请求在多个核之间摊开很多服务器默认开启。低延迟场景反而会关掉它把网卡队列和 CPU 绑定交给 RPS/XPS 这类更可控的手段。判断方法还是看mpstat的%soft有没有集中在某个核上。# 关闭自动分组让调度行为更可预期 echo 0 /proc/sys/kernel/sched_autogroup_enabledsched_autogroup是桌面交互场景的优化服务器上建议关掉尤其是跑数据库和消息队列时分组调度会引入额外的带宽波动。另一样容易被忽略的是 CPU 频率cpupower frequency-set -g performanceperformancegovernor 把 CPU 固定在较高频率减少频率升降带来的延迟波动。代价是功耗和温度上升风扇声会比性能瓶颈更先出现。对日志服务、监控采集这类负载平稳的任务用默认的powersave或schedutil就行对 RPC 服务、数据库这类延迟敏感任务才值得上performance。别忘了检查 BIOS 里的 C-state 设置内核层调完硬件层还在省电等于白调。4. 内存与 IO 调优swap、OOM 保护、IO 调度与挂载参数一起处理内存和 IO 的调优经常交织在一起内存回收会触发磁盘写回磁盘写得太慢又会反过来造成内存压力。在这一章里先把 swap 和缓存回收的策略讲清楚再给关键进程加 OOM 保护最后处理 IO 调度器和文件系统挂载参数。三者要一起看不能只动其中一个。4.1 看懂 swappiness 和 cache_pressure控制内存回收倾向vm.swappiness控制内核回收匿名页和文件页之间的倾向性默认 60。匿名页是进程的堆栈数据回收时得先换出到 swap文件页是 page cache回收时直接丢弃或回写文件。把swappiness调低表示内核尽量少换出匿名页调高表示更积极换出。# 查看当前值 sysctl vm.swappiness vm.vfs_cache_pressure # 临时调整 sysctl -w vm.swappiness10 sysctl -w vm.vfs_cache_pressure200我给一套常用来落地的值对数据库机器swappiness10左右避免数据页被换出对普通 Web 服务器保持 60 或降到 30对离线计算节点可以保持默认。vfs_cache_pressure默认 100控制 dentry/inode 缓存的回收强度调高会让内核更积极地清文件系统元数据缓存。文件数量巨大的服务比如 Git 仓库或静态资源服务器可以适当调高到 200减少元数据缓存占内存没有大量文件操作的进程不用动它。注意一个边界swappiness0不代表永远不 swap。内存压力到达临界点时内核照样会回收匿名页这个坑在第 5 章单独展开。4.2 用 oom_score_adj 和内存 cgroup 给关键进程加保险同一台机器上并行任务太多时内存被抢占OOM killer 很容易把数据库杀掉。数据库重启成本高损失可能不止几分钟。可以用oom_score_adj保护关键进程# 查看当前进程的 oom_score_adj cat /proc/pid/oom_score_adj # 给数据库进程打上保护标记 echo -500 /proc/pid/oom_score_adjoom_score_adj的取值范围是 -1000 到 1000数值越低越不容易被 OOM killer 选中。-1000 基本等于豁免但不要轻易给到 -1000万一真的内存耗尽系统可能因为杀不掉进程而卡死。我的习惯是数据库给 -500 到 -800中间件给 -200离线任务保持 0可热重启的批次任务给 200。OOM killer 的“值得杀”顺序可以用cat /proc/pid/oom_score查看。容器或 systemd 场景直接用 cgroup v2 的内存限制更干净echo 4G /sys/fs/cgroup/backend/memory.max echo 3G /sys/fs/cgroup/backend/memory.highmemory.max是硬限制超了会触发 OOMmemory.high是软限制超了内核开始回收但不会杀进程。线上调优时我一般先设memory.high观察回收频率确认不会频繁抖动后再压memory.max。给重要服务单独划出一个 cgroup好过把内存保护寄托在全局 oom_score 上。4.3 IO 调度器与挂载参数noatime、commit、ioniceIO 调优的第一步是搞清楚当前磁盘用的什么调度器cat /sys/block/nvme0n1/queue/scheduler echo none /sys/block/nvme0n1/queue/schedulerNVMe 和 SSD 建议用none也就是 passthrough 模式直接让块设备层把请求送到硬件队列。机械盘保留mq-deadline调度器帮忙合并排序能减少磁头寻道。虚拟机磁盘要分情况如果是本地虚拟化文件none通常没错如果是网络块存储可以先对比mq-deadline和none的延迟分布再决定。文件系统挂载参数对读写性能的影响同样直观。日志服务器、静态文件服务这类大量读文件的场景把 fstab 里的noatime加上/dev/mapper/vg-data /data ext4 defaults,noatime,nodiratime,commit60 0 2noatime让读文件时不更新访问时间省掉每次读取都写 inode 的开销nodiratime同理作用于目录。commit60把 ext4 每隔 5 秒刷日志的周期改成 60 秒减少小文件频繁写带来的 IO 次数。数据库场景不建议改commit因为日志落盘间隔变长断电时丢数据的窗口也变大。用 O_DIRECT 的数据库进程这些挂载参数影响会更小重点还是放在调度器和队列深度上。再给进程设置 IO 优先级ionice -c2 -n0 -p db_pid ionice -c3 -p backup_pid-c2是 best-effort 类-n0是最高优先级-c3是 idle只在磁盘空闲时跑。备份任务设成 idle就不会跟主业务抢 IO 带宽。5. 性能调优避坑指南四个常见翻车点与排查顺序这一章的四个案例都是我见过或亲手踩过的。它们有一个共同模式参数本身没有错错在改的时候只看局部忽略持久化、内存压力和硬件拓扑。每条按“现象、原因、解决”展开遇到类似问题时可以按这个顺序排查。5.1 改了 sysctl 却忘记持久化重启后一夜回到解放前给一台机器临时调了vm.swappiness10压测数据看起来很漂亮第二天机柜断电重启监控系统显示 swap 使用率又回到原来的样子。重启后/proc/sys下的改动全部丢失因为sysctl -w只改内存里的当前值不会写入任何配置文件。正确做法是直接写/etc/sysctl.d/下的独立文件cat /etc/sysctl.d/99-perf-tuning.conf EOF vm.swappiness10 vm.vfs_cache_pressure200 kernel.sched_autogroup_enabled0 EOF sysctl --systemsysctl --system会按顺序加载/etc/sysctl.conf和/etc/sysctl.d/*.conf重复的键以后加载的文件为准。建议所有调优参数都放进99-开头的文件里文件名带稳定数字前缀既方便排查也不会被发行版自动生成的其它 sysctl 文件覆盖。排查时用sysctl -a | grep核对当前值先确认是没生效还是被后面的配置文件覆盖。5.2 把 vm.swappiness 调成 0内存抖动反而更严重在一台 8G 内存的物理机上跑 Redis为了“绝对不让进程换出去”把vm.swappiness0写进了 sysctl 配置。结果跑了半天si/so里冒出了连续的换页值Redis 响应时间出现周期性尖刺。内存压力不是不存在了而是被积压到临界点后集中爆发。内核在物理内存耗尽前必然回收匿名页swappiness0只是让回收器更倾向于先回收文件页缓存。当文件页缓存已经被压到很低、又持续有大量写缓存时内核会卡在写回上表现为so持续大于 0、wa跟着升高。解决方式是按业务实际内存工作集留余地给 Redis 这类大内存进程留出常规占用 1.5 倍的内存swappiness设置在 10 上下再配合第 4 章的oom_score_adj-800。同时观察dirty_ratio和dirty_background_ratio的配合避免一次性刷太多脏页卡住整个 IO 链路。5.3 给 NVMe 换上 none 调度器数据库写日志反而变慢阵列卡上的 NVMe 盘默认调度器是mq-deadline看了博客说 NVMe 都该用none于是echo none /sys/block/nvme0n1/queue/scheduler。数据库的 redo log 随机小块写入延迟明显上升。原因出在这块盘前面的 RAID 卡带电池写保护硬件本身会做合并和续接mq-deadline反而帮它把小块请求先归类排序降低写放大换成none后小块请求的队列深度和合并逻辑变了反而放大了延迟。解决方式是按实测结果选调度器而不是按介质类型一刀切。用 fio 分别测none和mq-deadline下的随机写与混合读写例如fio --namerandwrite --ioenginelibaio --rwrandwrite --bs4k --size2G \ --runtime30 --time_based --direct1 --iodepth16 --outputtest.log对比两轮测试的平均延迟和 p99 延迟。数据库场景重点看小块随机写文件服务器重点看大块顺序读。结束之后把调度器写进 udev 规则或内核启动参数别只改了这一次。5.4 绑核绑到了另一个 NUMA 节点TPS 不升反降一台双路服务器上面跑 MySQL为了减少 CPU 调度抖动把线程用taskset -pc 0,8绑到了两个核上。结果 TPS 反而降了 20%。原因是 CPU0 和 CPU8 分别属于两个不同的 NUMA 节点MySQL 的内存分配器把数据放到了 node0线程却在 node0 和 node1 之间来回跑跨节点的内存访问延迟比调度抖动还贵。解决方式先用lscpu或numactl --hardware看清楚物理核属于哪个 socket、逻辑核怎么映射。绑定进程前先用numactl --membind0限制内存分配节点再配合taskset绑到同一个节点内的核上。比如 node0 有 CPU0-7就把进程绑到 0-3同时加numactl --membind0numactl --membind0 taskset -c 0-3 /usr/bin/mysqld_safeNUMA 场景判断并不难难的是默认以为所有核都一样。多路服务器上跨节点内存访问能轻易吃回绑核省下的调度开销所以绑核之前先看拓扑绑核之后再用实际服务压测数据说话。6. 调优后的验证路径把参数固化并用对比数据确认改动有效6.1 参数固化sysctl、grub、fstab 三层各改哪里调优最怕改完找不到记录。我一般分三层固化内核运行参数写进/etc/sysctl.d/99-perf-tuning.conf启动参数写在/etc/default/grub的GRUB_CMDLINE_LINUX里文件系统挂载参数写在/etc/fstab中。每一处改动都留一行注释说明原因日期也带上。这样做的好处是半年后回看能知道当时的决策依据而不是面对一堆参数猜动机。改完必须验证加载顺序sysctl --system对 sysctl 文件生效update-grub后重启才让内核启动参数生效fstab参数要mount -o remount /data或重启才能完全应用。检查实际值别信配置文件的字面值。6.2 用同一套基准测改动前和改动后而不是看感觉调优是否有效要在改参数前后跑同一套压力动作记录指标差异。最简单的是对 Web 服务跑一轮 ab再把延迟分布抓出来ab -n 10000 -c 50 https://your-server/health记录请求失败数、平均延迟、p99 延迟。数据库服务可以改用 sysbench 或 mysqlslap磁盘场景用 fio。更细的 CPU 行为对比用perf statperf stat -r 3 -e cycles,instructions,cache-misses -- /path/to/benchmark-r 3表示跑 3 轮取统计减少机器噪声影响。CPU 参数改动之后重点看 instructions 和 cache-misses 是否变化光看 CPU 使用率容易被频率调度掩盖。每次只改一个变量改完立刻测测完再改下一个。多个参数一起动出了性能回退根本定位不到是谁造成的。我自己的习惯是每台机器留一个“调优台账”把改前采样、改后采样、命令、日期贴在一起。半年后出问题先翻台账再翻配置。很多疑难问题不是调优本身翻车而是忘记了到底改过什么。希望这篇文章里的思路和命令能帮你把这套流程跑顺也尽量少走我走过的弯路。本文还有配套的精品资源点击获取
返回列表