机器卡了响应速度像被什么东西拖住一样。这是Linux服务器运维里最常见的求救信号但“系统慢”三个字其实什么也没说。我在实际排查中见过太多这样的情况有人上来就重启服务有人直接换机器配置还有人对着top输出发呆半小时。真正的问题藏在某个具体环节里——可能是CPU跑满了可能是内存换页换到飞起也可能是某个进程在疯狂写日志把磁盘IO堵死。这篇文章要分享的就是一套我从“系统慢”到精准定位的实战排查方法配合Linux性能排查的常用工具和指标解读让你下次遇到类似问题能按图索骥而不是瞎猜。先给个定调这篇文章适合谁如果你是刚接触Linux运维的开发、测试或初级运维那这套排查路径能帮你建立完整的排查思路如果你已经有一定经验那文中的一些工具组合和避坑细节也许能补齐你平时没太注意的盲区。我不会讲那种教科书式的原理堆砌而是直接从真实操作场景出发讲我怎么一步步缩小范围、确认根因。1. 定海神针先定边界再动手1.1 性能排查的通用套路从现象到指标再到根因排查“系统慢”最大的坑就是把排查顺序搞反了。我看到很多人一上来就盯着某个进程看半天或者拿着htop截图四处问完了发现方向从头就错了。实际上性能问题的排查有一套固定逻辑先确认瓶颈在哪个资源维度再针对该维度深入找具体进程最后才到代码或配置层面去解决。资源维度就四个CPU、内存、磁盘IO、网络。大多数“系统慢”都能归到这四类里或者是它们之间的间接影响。比如CPU跑满会导致请求排队内存不足触发swap会导致大量换页表现为CPU的sy占比升高磁盘IO饱和会让几乎所有读写线程都卡在D状态网络层丢包重传则会表现为响应迟迟不来。我的习惯是拿到问题后先花30秒看一眼全局指标确认方向再往下钻。这就像去医院先挂个全科门诊量体温、测血压医生判断你可能要看呼吸科还是消化科才会给你转到对应专科。直接让病人去做核磁共振是不负责任的。体现到命令上第一板斧就是uptime配合top。uptime直接给出1分钟、5分钟、15分钟的负载值这三个数的相对关系很有讲究——15分钟很高但1分钟降下来了说明是持续了一段时间的负载正在缓解1分钟和5分钟持续走高说明负载正在上升问题正在发生或恶化。这里有个容易踩的坑负载高不一定等于CPU忙。负载是所有正在运行和等待运行的进程数它可能是CPU等待也可能是不可中断的IO等待。所以看到高负载下一步一定要结合CPU的us、sy、wa、id这几个状态去看。工具选型这件事我不主张一开始就上复杂的监控平台。命令行三件套——top、vmstat、iostat——已经能覆盖90%的初步定位需求。它们的输出虽然朴实但信息密度极高每个字段背后都能牵出一条排查线索。1.2 第一组命令一条命令快速判断系统健康度我遇到“系统慢”的第一反应一定是依次敲这三条命令uptime top -n 1 vmstat 1 5uptime看负载走势top看CPU总状态和进程排行vmstat看动态变化。这三条命令串起来基本能把问题圈定在一个大致范围内。拿一个真实场景举例。假如我敲完uptime看到输出14:22:01 up 126 days, 3:14, 2 users, load average: 8.01, 6.35, 3.02load average三位数分别是1、5、15分钟均值。8.01这个值如果是4核机器意味着有8个进程在排队等CPU明显超载如果是32核机器那才用了四分之一负载完全正常。所以判断负载高低一定要跟CPU核数挂钩不能只看绝对数字。然后看top输出里的CPU状态行%Cpu(s): 12.5 us, 6.2 sy, 0.0 ni, 81.2 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st几个关键状态的含义我用大白话翻译一下us是用户态CPU占用程序逻辑代码在跑。sy是内核态CPU占用系统调用、内存管理、进程调度这些内核工作。wa是IO等待CPU在等磁盘或网络数据返回说明IO是瓶颈。id是空闲CPU没事干。st是被虚拟机管理程序偷走的CPU时间这个在云主机上要特别注意。通常wa高于30%就要高度怀疑磁盘IO有问题sy持续偏高可能是系统调用太频繁、内存换页或者上下文切换太剧烈光us高反而相对好办说明就是某个计算密集型进程在吃CPU。再配合vmstat 1 5看动态数据重点看r运行队列、b不可中断睡眠阻塞进程、si和soswap换入换出、waIO等待这几列。r持续大于CPU核数说明CPU过载b一列有值说明有进程卡在IOsi/so只要不是0就说明内存告急系统正在用磁盘当内存用这通常意味着要优先处理内存问题而不是CPU。这三个方向确认完再往下就是分维度的深入排查。下面几个章节我就按CPU、内存、磁盘、网络这四条主线分别讲各自的深入定位手段。2. 让CPU说话定位计算密集与调度问题2.1 用top/pidstat找到最活跃的进程与线程如果第一步确认瓶颈在CPU那么目标是回答三个问题哪个进程在消耗CPU它是用户态多还是内核态多是不是多线程导致的调度纷争top是首选工具进入交互界面后按P键按CPU占用排序基本一眼就能锁定头号嫌疑进程。但如果这个进程是Tomcat、Nginx这种多进程/多线程服务top默认只能看到进程汇总的CPU看不到具体是哪条线程在捣乱。这时候要按H键切换到线程视图让每个线程单独显示CPU占用。另一个更精确的工具是pidstat它能按进程或线程输出CPU使用率还能分用户态和内核态统计。命令如下pidstat -t -p PID 1 5-t表示显示线程级别-p指定进程号每秒采样一次、采样5次。输出里能找到具体线程的%usr和%system占比。我曾经排查过Java应用偶发CPU飙高的问题top里看到的进程CPU也就是200%多但切到线程视图后发现有一个线程长期占着150%左右的CPU顺着线程号去匹配Java的线程dump最终定位到某段正则匹配的代码在极端输入下触发了灾难性回溯。如果你不做线程级定位这段问题会非常难找。还有个细节在云服务器上经常看到ststeal值偏高。这是物理机上的其他虚拟机在争抢CPU资源不是你自己机器的问题。这时候你查自己机器的一切指标都是正常的但服务就是慢大概率是宿主机超卖导致邻居吵闹。遇到这种情况真没法通过优化代码解决只能迁移实例或升级资源。另外提醒一句top里的VIRT和RES别混淆。VIRT是进程申请的虚拟内存总量只是一个地址空间的概念不完全代表真实占用的物理内存RES才是实际驻留在物理内存中的部分。判断内存占用要以RES和后续的free命令输出为准。2.2 多核视角与上下文切换拆解sy占比过高的谜团CPU的sy过高是一个比较隐蔽的方向。us高说明业务代码在忙sy高说明内核在忙。最典型的sy高的原因有三个一是系统调用太频繁比如小数据量的大并发读写二是内存换页vmstat里si/so有值页面反复在内存和磁盘间搬运三是上下文切换太剧烈大量线程在抢占CPU。排查上下文切换用vmstat看cs列或者用pidstat -w看具体进程的主动/被动切换次数。命令pidstat -w -p PID 1 5这里有个判断经验单核每秒上下文切换在几千次以内算正常但如果飙到几万甚至几十万那肯定是有调度风暴了。常见的元凶是线程数量过多的线程池、锁竞争激烈导致的自旋等待、或者定时器密集触发。看一个实际现象。我排查过一个系统CPU总体才用30%但sy占了25%响应时间却高得离谱。用pidstat -w一看某个进程每秒的非自愿上下文切换达到3万多次而它本身的线程数不过几十个。顺着这个方向查下去最后发现是多路复用库的选择器在极端连接数下频繁唤醒导致线程不断让出CPU又被唤醒。问题解决后sy瞬间掉到5%以下。这里其实也暴露了一个排查哲学CPU占用率不高不代表系统不慢。调度抖动造成的响应延迟往往比单纯的高CPU占用还难缠。因为从资源总量看CPU还有大量空闲但局部热点线程就是抢不到时间片。2.3 深入热点函数perf top与软中断排查到了这一步如果还要继续深挖就得从“哪个进程”升级到“哪段代码”。perf top是一个能直接看到CPU热点函数的工具perf top它会把采样到的CPU执行栈按占比排列直接告诉你CPU时间都耗在哪个内核函数或用户态函数上。比如看到热点是do_sys_open或ext4_file_read_iter那就说明文件操作相关逻辑很热看到queued_spin_lock_slowpath那意味着锁竞争严重。perf这类工具对生产环境比较讲究有些环境出于安全或性能考虑不允许加载内核模块但也有折中方案。至少可以用perf top --no-inherit或限制采样频率来降低侵入性。如果实在不允许用perf退而求其次可以借助strace -c -p PID统计进程的系统调用耗时分布。strace的副作用是会让目标进程大幅变慢所以生产环境慎用先做好心理预期再操作。软中断处理是另一个常见的CPUsy偏高原因尤其在网络流量大的机器上。查看每个CPU上的软中断累计数据可以看/proc/softirqs关注NET_RX、NET_TX这类网络收发条目是否集中在某一个CPU上。如果网络的软中断都被打到一个核该核的sy会很扎眼但整机整体CPU却不高。这种情况要么调RPSReceive Packet Steering做软中断负载均衡要么靠网卡多队列配合中断绑核来解决。每颗CPU核上的软中断情况用mpstat -I SUM 1看也很直观。3. 内存真相区分真正的压力与应用缓存3.1 读free别被cache吓到内存方向排查的第一误区是看到free命令里available很低就以为内存不够了。Linux的内存管理哲学是“内存闲着也是闲着不如用来做缓存”。大多数内存都被用来当作page cache读缓存、写缓存这部分内存在应用需要时可以随时回收。free命令输出的buff/cache那一大块并不是泄漏更不是浪费。在free -h里真正要关注的是available列。这个值代表“在不触发swap的情况下还能给新进程分配多少内存”。available已经扣除了page cache可以收缩的部分比free列的参考价值大得多。如果available长期见底并且vmstat里出现了si和so非零的情况那才说明内存真的吃紧。si是swap换入so是swap换出。只要这两个值持续跳动说明系统已经把一部分进程的物理内存页换到磁盘上进程访问这些页面时又要从磁盘读回来。这个换进换出的过程远比内存本身更拖慢速度。关于内存总量的判断还要考虑进程本身的RSS。top里的RES总和并不能直接和总内存对比因为有共享内存的存在。更准确的汇总可以用smem或者smem -t -k它会按比例分摊共享内存统计每个进程真正“私有按比例共享”的占用。这在容器或Java等大量使用共享类库的场景下特别有用。3.2 内存回收机制与OOM风险观察free只展示某个瞬间的快照。要观察内存压力的动态趋势用vmstat或sar -r看长时间序列更靠谱。sar -r 1 5sar -r输出里kbcommit是当前已经提交的内存总量%commit是这个提交量占总内存加swap的比例。这个比例长期超过100%意味着系统有超卖风险后续申请内存时会被卡住。还有一个关键指标藏在/proc/meminfo里Dirty和Writeback。Dirty表示待写入磁盘的脏页数量。Dirty不断上涨而Writeback很大说明系统后台刷盘跟不上应用写脏页的速度最终会触发强制回收这时候应用会被卡在等待内存页回收完成表现为进程突然“僵住”几百毫秒甚至几秒。应对方式是检查是否有进程在大量随机写文件或者考虑调整vm.dirty_ratio和vm.dirty_background_ratio的阈值但这两个参数涉及全局行为线上环境改之前一定要在测试环境充分评估。真正的高危场景是OOMOut Of Memory。当内存耗尽且swap也用尽内核的OOM Killer会挑进程杀掉。如果你发现某个服务莫名其妙挂了第一时间查dmesg或journalctl -k看有没有Out of memory字样的记录dmesg | grep -i out of memory我见过一个典型的案例某台机器上跑了好几个Java服务每个都设置了很大的堆内存加上本地缓存物理内存远不够用。某个凌晨流量上来后内核直接OOM杀掉了其中一个非关键进程来救急。整个页面没有报错服务就像凭空消失了一样。如果不看内核日志这个问题会浪费很久去排查。3.3 查内存大户smem与进程级定位细节找到消耗内存最多的进程大多数人会用top然后按M排序。这个操作没问题但有一个坑top按RES排序时把所有共享库内存都算在了每个进程头上会高估共享库依赖重的进程。比如多个Java进程共享同一个JVM类库这些共享页会重复计入各自RES里看起来每个都吃了好几个GB实际总占用并没有那么多。要精确计算进程“独占按比例共享”的内存占用用smemsmem -t -k -s rsssmem会列出按比例分摊后的PSSProportional Set Size值这个数值比RES更接近真实占用。对于定位内存泄漏我习惯每隔一段时间记录一次PSS变化趋势关注持续只涨不降的进程。内存泄漏不会瞬间暴露通常是慢慢把available“吃”光。如果是Java应用还要区分JVM堆内和堆外内存。JVM堆内可以通过jstat -gc PID看堆使用情况堆外则包括线程栈、JIT编译代码、直接内存DirectBuffer、Metaspace等。有些内存问题并不在堆内——比如NIO的DirectBuffer使用不当会导致堆外内存持续增长最终把系统内存耗尽但从堆内看一切正常。排查这类问题jcmd PID VM.native_memory能给出比较细致的Native Memory Tracking报告前提是在JVM启动参数里开启了-XX:NativeMemoryTrackingsummary。4. 磁盘与文件IO当系统卡在“等IO”4.1 iostat解读util/await/svctm到底意味着什么磁盘IO导致的“系统慢”非常有辨识度——CPU的wa飙高、进程大量进入D状态、vmstat的b列有值。但磁盘慢还分好几种情况是磁盘本身处理不过来还是单次IO太大太慢又或者是文件系统/逻辑卷层面的问题需要用iostat把变量拆开。iostat -x 1 5iostat -x扩展输出里核心指标见下表指标含义我的判断参考%util设备处理IO请求的时间占比持续高于80%通常意味着磁盘饱和awaitIO请求从提交到完成平均耗时毫秒机械盘几十毫秒正常SSD应低于几毫秒svctmIO请求的服务时间毫秒不是平均是近似值现在已不推荐重点参考r_await/w_await读/写各自的平均等待时间写延迟高与读延迟高的解决方向完全不同avgqu-sz设备队列长度持续很大说明IO请求堆积有个常见陷阱%util达到100%不一定就是磁盘硬件瓶颈特别是对单块SSD而言。因为%util计算的是“设备有IO请求在处理的采样时间占比”不代表设备真的没有能力处理更多请求。有些SSD在大量小文件随机读写时%util很高但本质是并发队列深度设计导致带宽没打满实际硬件还有冗余。所以不要单看util要结合await一起来判断。await高而svctm并不高基本可以确定IO请求在队列里等待过久说明系统侧的IO压力已经超过设备侧的处理速度。这里有个反直觉的经验svctm这个指标在新版iostat里不建议过度解读。svctm是通过计算得到的近似均值在多队列设备上已经失真很多内核版本甚至不保证它的正确性。我更倾向于直接用await配合队列长度来看延迟用r/s、w/s、rkB/s、wkB/s来看吞吐量。4.2 用iotop和perf trace找到IO杀手知道磁盘很忙了下一步要问“谁在写/读”。iotop是排查IO进程的最直观工具相当于IO版top。运行iotop -o只看正在产生IO的进程能直接看到每个进程实际的读/写速率单位是KB/s。iotop -o -P-P是显示进程而非线程-o只显示有IO操作的进程。如果系统里IO请求过多iotop本身的采样进程会跟被测进程争抢IO所以生产环境里观察时间不要拉太长记录几秒结果就够。iotop看到的是实时速率如果要关联到文件级别可以借助lsof或者/proc/PID/fd去查看进程打开的哪些文件在被频繁读写。很多实际问题不仅是“某进程IO很大”而是“某进程在某个路径下疯狂读写”。我处理过一个日志服务拖垮整机的案例某个服务的info日志设置了DEBUG级别日志框架每秒写几百条日志每条都带完整堆栈。iotop显示这个进程写速率不超过2MB/s看着并不惊人但每个IO请求都很小、很碎直接把底层磁盘的IOPS打爆await飙到100ms以上。这种小碎IO比大块连续写更有杀伤力因为磁盘处理IO是看请求数量而非请求大小的。没有perf权限时还可以用pidstat -d快速查看每个进程的读写速率pidstat -d -p PID 1 5pidstat -d输出KB_rd/s、KB_wr/s虽然没有iotop直观但胜在不需要额外安装工具而且能做时间维度上的对比。4.3 系统日志与文件系统同步导致的周期性卡顿IO层面的问题不光是进程直接读写文件系统自身的机制也会制造延迟。最常见的是日志文件系统比如ext4/xfs的提交commit行为——为了保证数据一致性文件系统会周期性把日志刷到磁盘这个周期默认可能是5秒。如果某一次日志提交特别慢所有等待该文件系统IO操作的进程都会跟着被卡住表现为服务每隔几秒出现一次明显抖动的“锯齿状”延迟曲线。iostat能帮你捕捉这种现象await呈现规律的周期症候群每几秒出现一次尖峰其余时间都很低。这比持续高await更隐蔽——它不构成整机高负载但对那些对延迟敏感的服务比如网关、数据库中间件影响非常明显。这类问题的排查方向有几个检查文件系统挂载参数确认是否启用了barrier或dataordered这类一致性优先的选项。对交易类数据这是必要的安全取舍但对允许丢失部分日志的缓存类数据可以考虑调整为性能优先模式。确认底层存储的刷盘能力。如果机械盘做RAID且有大量随机写日志提交的等待时间大概率降不下来。看/proc/meminfo里的Dirty值配合vmstat的wa列判断是否频繁触发后台刷盘。还有一类磁盘慢是硬盘本身坏道或固件问题导致的。当iostat显示await高但util并不饱和且dmesg里出现sda相关IO错误、重置reset记录时最好联系基础设施团队做硬盘健康检查。这类硬件问题用软件层面手段无法根除只能提前发现、提前更换。5. 网络层面的慢连接与重传5.1 先看队列再抓包ss与accept队列的隐蔽瓶颈网络问题的排查顺序我习惯先看socket队列再看到达率/丢包率最后才是抓包。很多“慢得离谱但CPU、内存、磁盘都正常”的场景根源都在连接队列。每个监听socket都有两个队列recv-Q对应的半连接队列SYN Queue和send-Q对应的全连接队列Accept Queue。当客户端请求进来内核完成三次握手前先放在半连接队列握手完成后移入全连接队列等待应用调用accept()取走。如果应用处理速度跟不上全连接队列会持续堆积新连接被丢弃客户端表现为“连上了但响应特别慢”甚至连接失败。用ss -lnt查看监听端口的队列状态ss -lnt输出中Recv-Q和Send-Q在监听套接字上分别表示当前已建立但未accept的连接数和队列容量上限。当Recv-Q长期接近Send-Q的容量上限时说明应用accept的速度跟不上建连速度。这一问题排查起来很容易被忽视因为CPU、内存、磁盘全都没有瓶颈压力抓包也看不出明显的异常报文但用户的感受就是慢、偶尔还连不上。这时候真正的解法往往在应用层检查线程池是不是太小、是否没有开启连接复用导致频繁新建连接、是否存在慢消费。如果应用层优化空间不大可以临时调大net.core.somaxconn和应用的backlog参数但这是缓解而不是根治。5.2 网卡软中断与TCP重传排查sAR与netstat双视角网络排队不只是连接队列还包括网卡队列和软中断下发。如果单核sy飙高且集中在某一个CPU核上排查网络软中断方向会很有价值。看趋势sar -n DEV 1 5 sar -n TCP,ETCP 1 5sar -n DEV看各网卡rxkB/s、txkB/s和%ifutil判断流量是否接近端口带宽上限。sar -n TCP里的active/s和passive/s代表主动/被动建连速率retrans/s是TCP重传速率。重传率是判断网络质量的重要指标——如果retrans/s持续占比过高丢包问题会直接影响传输吞吐表现为下载/同步慢、响应延迟大。配合netstat -s看累计统计netstat -s | grep -i retrans重传的原因多种多样网络线路本身不稳、网卡驱动错误、或对端排队溢出导致接收窗口不足。看到重传升高可以用ethtool -S 网卡名看网卡丢包和FCS错误等统计也可以tcpdump抓包看是不是有大量重复的ACK。如果确认是链路层丢包能做的更多是跟网络团队联动。还要注意TIME_WAIT和CLOSE_WAIT堆积。TIME_WAIT多通常是大量短连接快速关闭系统在等待2MSL超时回收连接资源。CLOSE_WAIT多则说明应用被动关闭连接后没有正确调用close属于应用层的连接泄漏会慢慢耗尽文件描述符并导致新连接建立失败。5.3 结合应用日志与负载均衡配置缩小范围网络慢还可能是负载均衡层造成的这属于部署架构层面的排查。有个经验如果ss、sar都看不出异常但用户实际访问的入口是外部LB接入那要在LB的转发层看监控。某些LB在连接数高时转发性能会下跌导致接口响应变慢。判断办法是找一台绕过LB、直连后端的机器在同样的时间里对比接口耗时就真相大白了。另外一个在应用层面经常被忽略的指标是“排队时间”。如果应用能输出访问日志观察耗时分解——连接建立时间、请求处理时间、响应传输时间——能快速区分网络问题和代码问题。处理快但传输慢就去看接收窗口和带宽请求在应用内部排队就先排查线程池和锁。我用过一个小技巧改造监控埋点时在客户端侧记录curl -w的时间明细。curl -w可以输出time_namelookup、time_connect、time_starttransfer几个分段它们分别对应DNS解析、TCP建连、首个字节返回的耗时。如果time_connect偏大而服务端处理很快基本就锁定在链路和连接队列上如果time_starttransfer偏大就要看后端的应用处理瓶颈在哪。这种做法在排查“从入口就感觉慢”的时候特别实用。6. 排查工具箱与实战速查6.1 一套实用的排查次序组合拳工具不在多在于组合使用。为了便于复现我整理了一套我个人最常用的排查次序按“全局观察 → 定向深挖 → 根因确认”三个阶段来排。第一阶段5分钟内完成uptime # 负载走势 top -b -n 1 | head -20 # CPU状态 进程排行 vmstat 1 5 # 运行队列/换页/IO等待/上下文切换 free -h # 内存总览 iostat -x 1 3 # 磁盘与IO延迟 sar -n DEV,TCP,ETCP 1 3 # 网络流量/建连/重传这套打完后你大概能得出结论瓶颈在CPU、内存、磁盘还是网络。如果看了这组输出还判断不准方向说明需要拉长时间窗看趋势可能问题不是持续性的而是周期性的。这时候把命令的输出追加保存到文件比如top -b -n 300 top.log再配合watch命令观察持续抓几分钟再分析。第二阶段定位到具体进程/线程pidstat -t -p PID 1 5 # 线程级CPU拆分 pidstat -w -p PID 1 5 # 上下文切换统计 pidstat -d -p PID 1 5 # 磁盘读写速率 iotop -o -P # IO进程排行 ss -lnt # 连接队列状态第三阶段确认根因perf top # CPU热点函数 perf record -g -p PID sleep 10; perf report # 采样调用栈 strace -f -p PID # 系统调用级追踪慎用 dmesg | tail -100 # 内核日志OOM/IO错误 dmesg | grep -i out of memory工具组合的关键不是命令多而是每一步都带着明确问题去执行。看到什么指标、怀疑什么方向、用什么工具验证这个链路要闭合否则就是收集了一堆数据但没有结论。6.2 容器与虚拟化环境下的特殊注意点现在很多业务跑在容器里性能排查和物理机有显著差异。容器里看到的CPU占用率已经是宿主机限制后的结果。容器内的top显示100%CPU指的是容器被分配的CPU配额已经用满而不是宿主机全部CPU的100%。概念上这和水龙头限流一样——容器内看到的水流量是在水龙头限流阀之后的流量限流阀开多大和你家水管总口径不完全对应。所以容器里排查CPU慢优先看配额与需求的关系。cpu.cfs_period_us和cpu.cfs_quota_us两个cgroup参数决定容器最多能用多少CPU时间。如果你给容器分配了2核但业务高峰需要4核那/sys/fs/cgroup/cpu里的throttled_time会持续增长容器内一切指标都正常但就是慢。用cat /sys/fs/cgroup/cpu.stat查看nr_throttled和throttled_time能确认CPU是否被限流。容器内存同理/sys/fs/cgroup/memory/memory.limit_in_bytes是容器内存限额。容器内free看到的物理内存是宿主机总量不代表容器能用到那么多。应用在容器内认为内存充足一旦超过cgroup限制会触发容器OOM或被内存回收影响。检查容器OOM时不光看dmesg还要看/sys/fs/cgroup/memory/memory.events里的oom计数。虚拟化平台上的st值我前面提到过是另一个需要留意的点。虚拟机里发现CPU忙但执行效率低且%st占比高基本可以确认为宿主资源争抢。这类问题优化自身代码意义不大优先考虑迁移到空闲宿主机或提高资源配额。6.3 内核参数调优的边界与风险排查到最后经常会有人提议“调内核参数试试”。我的态度是调优前先量化收益确定调什么用什么指标证明有效。Linux内核参数几千个每个参数的牵涉面都很大缺乏明确数据支撑的盲目调优往往带来更大的问题。拿最常见的几个参数举例vm.swappiness控制swap倾向。设为0不意味着禁用swap而是在内存仍有充足可用page cache时优先不换出内存页。对于延迟敏感的在线服务适当调低可以降低换页风险但调成0后如果内存真的耗尽系统直接进入回收模式反而可能造成更长的停顿。net.core.somaxconn调整accept队列长度。这个参数需要配合listen()的backlog参数才会生效只调一处往往没效果。调太大也不会让处理能力变强只是让堆积可以更多。vm.dirty_ratio与vm.dirty_background_ratio控制脏页刷盘阈值。把dirty_ratio调大的初衷是让写操作先缓存在内存中但代价是掉电或崩溃时丢数据风险加大以及内存回收时一次要回写大量脏页产生更长的卡顿。fs.file-max这是全局文件句柄上限和ulimit -n的进程级限制是不同的维度。很多“文件打开失败”问题实际上是进程级限制耗尽调全局参数并不会解决问题。正确姿势是先记录当前指标基线以“改一个参数、观察一段时间、对比基线、回滚问题变更”流程来操作。比如怀疑Dirty持续高位导致周期性卡顿那么先记录当前的sar -B中的pgscank/s和pgsteal/s修改参数后再对比这两个指标是否有明显回落同时观察应用延迟曲线有没有变平滑。没有指标对比的调优纯粹是玄学。7. 避坑总结与经验分享7.1 常见误区清单这节梳理几个我见过频率最高的排查误区每一个都是真实踩过坑之后的教训。第一个误区是“只信top不信趋势”。top默认输出是瞬时值负载飙高的瞬间你看一眼top可能刚好遗漏了罪魁祸首。排查必须有时间维度让命令持续记录几秒到几分钟最好落在日志文件里回头分析变化趋势。第二个误区是“用free判断内存不够”。准确的判断方式是看vmstat的si/so和available趋势而不是free列和buff/cache的绝对数字。不要看见buff/cache很大就误以为是缓存泄漏。第三个误区是“高负载就必须看CPU”。load average包含等待IO的进程数高负载但CPU很空闲说明进程卡在IO上。遇到这种矛盾优先看磁盘和网络而不是长时间盯在CPU视图。第四个误区是“iostat%util100%一定代表磁盘硬件不行”。util在有NVMe和复杂RAID控制器的场景下会失真要结合await、队列长度和实际吞吐一起看。出现util高同时await不高的情况判断方向更侧重IO调度策略而不是硬件能力。第五个误区是“排查问题从抓包开始”。抓包是确认细节的手段不适合作为第一步。先建立起系统资源的整体画像先知道大概方向再去抓包验证网络层细节否则面对海量报文只会更懵。第六个误区是“忽略内核日志”。dmesg里藏着OOM记录、IO错误、网卡reset、文件系统错误这些都是系统级问题的直接证据而且往往不会体现在业务监控里。当业务监控一片正常但问题真实存在内核日志常常是第一突破口。7.2 一次真实案例的复盘思路最后分享一个我印象很深的案例完整复盘一遍排查路径算是把整个方法论串一遍。某天下午某个业务接口的P99延迟从50ms飙到了3000msCPU、内存监控曲线都没有明显异常只有负载从4涨到了8。我先跑uptime确认负载在爬升再看vmstat 1 5发现b列有值wa达到60%以上——方向直接指向磁盘IO。再用iostat -x 1 5看到await持续在200ms以上%util接近100%基本确认磁盘出问题了。接着iotop -o -P发现只有一两个进程在大量写数据顺着PID的/proc/PID/fd定位到它们在写某个数据目录下的临时文件。进一步确认那个目录挂载的是普通机械盘而且RAID卡驱动出现过多条I/O error日志。最后排查出是某后台任务在批量清理数据时产生了大量随机小文件删除加上机械盘老化IO延迟雪上加霜。解决方案是把这个清理任务迁移到高性能存储分区同时错峰执行。改完后wa迅速回落P99恢复。整个过程中的关键转折是第一组vmstat里b列和wa的异常它在CPU监控完全正常的情况下把方向引到了IO。这也是我为什么反复强调从第一分钟就要用全局指标做分诊因为“所有监控正常”这种感觉往往只是因为你还没看对监控。我个人在实际操作中的体会是性能排查就像剥洋葱每剥开一层都要用对应的工具验证一下方向是不是对的。方向错了手段再高级也白搭。所以别急着把perf、strace这些高级工具一上来就搬出来先让uptime、vmstat、iostat这三板斧帮你把范围缩小。这次从“系统慢”到精准定位的实战总结希望能让你的排查路径少绕几个弯。