Linux 线上性能排查实战指南:CPU · 内存 · 磁盘 IO · 网络的系统化定位方法
0. 简介线上机器一旦变慢最怕的不是问题复杂而是人一慌就开始乱猜。真正有效的排查不是上来盯着某个进程猛看而是先拿到全局快照再把异常收敛到 CPU、内存、磁盘 IO 或网络中的一条主线最后定位到具体进程、线程、系统调用或连接状态。这篇指南讲的不是零散命令而是一套真正能落地的排查顺序——从 PSI 压力指标到容器环境从 sar 历史回溯到级联故障识别覆盖现代 Linux 运维排查的完整链路。监控上显示接口延迟从50ms突然涨到3s错误率开始抬头机器 CPU 告警。你连上服务器终端光标一闪一闪脑子里却不一定立刻有答案。很多人这时候会直接猜是不是流量暴涨了是不是数据库慢了是不是有人把日志开成了 DEBUG是不是内存泄漏了。猜测当然有时会猜中但大部分线上排查真正浪费时间的地方恰恰就是“太早下结论”。服务器性能问题表面上看都是“变慢了”但底层成因并不一样。CPU 打满会变慢内存回收压力大也会变慢磁盘排队会变慢网络链路抖动还是会变慢。更麻烦的是这几类问题在监控上经常会互相伪装。比如磁盘 IO 排队会把load average拉高内存紧张导致频繁回收也可能表现成 CPU 抖动网络发送缓冲堆积又可能看起来像应用线程卡住。所以线上排查最重要的不是“我会多少命令”而是“我能不能先判断主矛盾在哪一层”。1. 先建立全局视角而不是上来就猜线上机器出问题以后第一反应应该是“先拍快照”而不是“先判断根因”。这里的所谓全局视角核心就是两件事先看机器现在整体处在什么状态。再判断 CPU、内存、磁盘 IO、网络哪一条线最先失控。如果这一步做对了后面的排查会越来越窄如果这一步做错了后面的努力大概率是在错误方向上深挖。1.1 为什么第一眼不能只看top很多人收到报警后第一条命令就是top这没错但如果只看top信息是不够的。top能回答“当前谁最忙”却不一定能回答“为什么忙”。一个进程 CPU 很高可能真的是业务计算吃满也可能只是别的资源拖住了系统导致这个进程看起来异常活跃。更关键的是load average并不等于 CPU 使用率。Linux 会把可运行任务和不可中断等待任务一起计入负载所以磁盘 IO 排队同样会把负载顶上去。这就是为什么第一组命令最好不要只有一个top而应该是下面这套dateuptimetop-b-n1|head-n25vmstat15iostat-x-y15ss-s这组命令的价值不在于“全面”而在于它们互相补位。date用来记录故障时刻后面要对日志、监控、链路追踪时很有用。uptime快速看负载是否突然抬高。top看进程层面的忙碌情况。vmstat看 CPU、内存、换页、块 IO 是否同步异常。iostat判断磁盘是否真的在排队。ss -s看连接状态有没有明显堆积。1.2 拿到快照以后先问自己四个问题load average高是 CPU 真不够还是有大量任务在等 IOMemAvailable是不是真的在掉还是只是页缓存变大了磁盘到底是忙还是已经排队到影响前台请求网络到底是连接数太多还是某些连接的 RTT、重传、队列出了问题只要把这四个问题依次问完方向通常就不会偏。1.3 全局判断时最先看的几个信号指标优先级它回答什么load average高机器整体压力大不大%Cpu中的us/sy/wa/id高压力更像来自计算、内核还是等待vmstat里的r/b/si/so高任务是在抢 CPU、等 IO还是开始换页iostat里的await/aqu-sz/%util高磁盘是否已经明显排队ss -s高TCP 状态是否堆积其中最容易误判的是两个地方。第一load average高不等于 CPU 满。第二wa高也不能单独下结论说“磁盘有问题”因为iowait本身并不是一个足够可靠的单指标必须配合iostat、vmstat和进程级 IO 再判断。1.4 一分钟内怎么做初判现象更像什么先去哪条线ussy高id很低r也高计算或系统调用压力CPUMemAvailable持续走低si/so不为 0内存紧张甚至开始换页内存wa高b高await高aqu-sz高磁盘 IO 排队磁盘 IOTIME-WAIT、CLOSE-WAIT、SYN-RECV明显异常连接模型或链路问题网络1.5 用 PSI 做更精确的压力判断上面的初判方法已经够用了但如果你的内核版本在 4.20 以上还有一个更直接的工具可以用PSIPressure Stall Information。PSI 的核心价值在于它不是告诉你某个资源的使用率是多少而是直接告诉你有多少比例的时间任务因为等待这个资源而被卡住了。这比传统指标更接近业务体感。cat/proc/pressure/cpucat/proc/pressure/memorycat/proc/pressure/io每个文件会输出类似这样的内容some avg104.50 avg602.80 avg3001.20 total18923456 full avg100.00 avg600.00 avg3000.00 total0这里面最关键的两个概念some至少有一个任务因为等待该资源而被阻塞的时间占比full所有任务都在等待该资源、没有任何有效工作在进行的时间占比判断标准可以这样理解指标含义什么时候该紧张cpu some avg10 20最近 10 秒内有明显的 CPU 争抢持续高于 25 就值得关注memory some avg10 10内存回收压力已经开始影响任务持续高于 20 要警惕memory full avg10 5所有任务都在等内存回收这已经是严重状态io some avg10 15IO 等待开始拖慢部分任务持续高于 30 需要立刻排查io full avg10 10所有任务都卡在 IO 上磁盘大概率已经成为瓶颈PSI 的好处是它把资源紧张程度量化成了一个统一的百分比不需要你自己去交叉对比多个指标。在全局初判阶段先看一眼 PSI往往能比传统方法更快地锁定主矛盾方向。需要注意的是PSI 在某些老内核或精简内核上可能没有开启。如果/proc/pressure/目录不存在说明内核编译时没有打开CONFIG_PSI这时就只能回到传统方法。1.6 用 sar 回溯故障时段的历史数据线上排查有一个非常现实的困境你赶到现场时故障可能已经过去了。top、vmstat、iostat这些命令都是实时采样它们只能告诉你现在怎么样但如果问题发生在 10 分钟前、半小时前甚至昨天凌晨这些命令就无能为力了。这时候sar就非常关键。sar是sysstat包的一部分它会定期把系统指标写入二进制日志文件通常在/var/log/sa/或/var/log/sysstat/你可以事后回溯任意时间段的数据。# 查看今天的 CPU 使用历史每 10 分钟一个点sar-u# 查看今天的内存使用历史sar-r# 查看今天的磁盘 IO 历史sar-d-p# 查看今天的网络流量历史sar-nDEV# 查看今天的负载历史sar-q# 查看指定时间段比如下午 2 点到 3 点sar-u-s14:00:00-e15:00:00# 查看昨天的数据sa 文件按日期编号sar-u-f/var/log/sa/sa$(date-dyesterday %d)sar在排查中最常用的几个场景故障已经恢复但你需要确认故障时段到底发生了什么。需要对比正常时段和异常时段的指标差异。需要判断某个指标是突然飙升还是缓慢爬坡——这对根因判断非常关键。需要向团队或管理层提供故障时间线的数据支撑。一个实用技巧如果你发现机器上sar没有数据大概率是sysstat没有安装或者 cron 任务没有启用。建议在所有生产机器上确保sysstat已安装并且/etc/cron.d/sysstat处于激活状态。这是一个成本极低但关键时刻能救命的配置。打开配置文件sudonano/etc/default/sysstat找到 ENABLED“false” 这一行将其修改为 trueENABLEDtrue(或者你可以直接运行这条命令一键替换sudo sed -i s/ENABLEDfalse/ENABLEDtrue/g /etc/default/sysstat)2. CPU 排查先判断它是不是“真 CPU 问题”很多机器报“CPU 告警”时真正的问题未必是 CPU。这是线上排查里非常常见的误区。因为在监控系统里CPU 和负载通常是最早出现在大盘上的所以大家很容易本能地朝 CPU 方向钻。但 Linux 的负载统计不是“CPU 百分比”那么简单大量等待磁盘 IO 的任务也会进入负载。所以你看到load average很高时第一反应不应该是“扩容 CPU”而应该是“先分清这些任务到底在跑还是在等”。2.1 CPU 这一段先看四个点top-b-n1|head-n20vmstat15mpstat-PALL15psaux--sort-%cpu|head-n10这四条命令分别在回答不同的问题。top看总量和热点进程。vmstat看可运行队列r、阻塞任务b、上下文切换cs。mpstat -P ALL看是不是只有少数几个核被打满。ps看哪些进程最吃 CPU。2.2us、sy、wa分别意味着什么很多人会背这几个缩写但真到排查现场不一定知道该怎么用它们判断。us高说明 CPU 时间主要花在用户态常见于业务计算、循环、序列化、压缩、正则、GC。sy高说明内核态开销偏大常见于系统调用密集、网络包处理开销大、锁竞争严重、频繁fork。wa高说明 CPU 有相当一部分时间在等 IO 完成但这时不能只看 CPU 面板必须继续结合磁盘维度看。把这三个指标分清楚排查速度会快很多。因为它们决定了你下一步要不要继续看 CPU还是应该转向 IO 或网络。2.3 什么时候才算“真 CPU 问题”一般来说下面这组信号同时出现才更像是 CPU 本身成了主瓶颈ussy很高id很低vmstat的r长时间明显大于核数mpstat -P ALL显示多个核持续繁忙没有明显的高wa、高b、高si/so如果这些条件不满足就不要急着把锅扣到 CPU 头上。2.4 一旦确认是 CPU 方向顺序怎么走psaux--sort-%cpu|head-n10top-H-pPIDpidstat-t-u-pPID15ps-eLopid,ppid,tid,psr,pcpu,stat,comm--sort-pcpu|headperftop-pPIDstrace-f-tt-T-pPID-c这个顺序不要乱。先找进程再找线程再看函数热点最后才去看系统调用统计。因为很多时候问题在perf top那一步就已经收敛了没必要一上来就进入最重的观察手段。2.5 三种最常见的 CPU 高场景2.5.1us高说明算力真的被业务吃掉了这种场景最典型。比如某段代码里有死循环某个正则表达式触发了灾难性回溯某个请求路径引入了巨大的 JSON 序列化开销或者 Java 程序在高压下频繁 Full GC。它们的共同点是CPU 时间主要耗在用户态热点通常集中在少数业务线程上。这时候最有用的命令是top-H-pPIDperftop-pPIDtop -H解决的是“哪个线程最热”perf top解决的是“热在哪里”。前者帮你缩小对象后者帮你收敛到函数。2.5.2sy高说明开销大头在内核态这类问题比单纯的业务计算更隐蔽。如果你看到sy特别高通常应该怀疑系统调用过于频繁网络小包风暴日志写得非常碎futex很多说明锁竞争明显频繁open/close/stat这时用strace -c很有效strace-f-tt-T-pPID-c如果你看到write、recvfrom、sendto、futex、epoll_wait这些调用占比异常高就不要再停留在“CPU 高”这个表象上了而要追问为什么这些系统调用会这么密集。2.5.3 负载高但 CPU 没满这是最容易让人误判的一种情况。表面看机器“负载爆了”实际上真正忙的不是 CPU而是别的资源。常见信号是load average很高ussy没那么夸张wa较高或者vmstat里的b很高这时候如果你继续在 CPU 上深挖大概率只会越查越偏。正确动作是立刻转到磁盘 IO 或锁等待链路。2.6 CPU 方向最常见的四个误判把load average当成纯 CPU 指标。看到某个进程%CPU高就不再往线程层细分。只看总 CPU不看mpstat -P ALL从而错过单核瓶颈。在虚拟机里忽略st误把宿主机争抢当成业务退化。3. 内存排查分清是泄漏、缓存还是回收压力内存问题看起来比 CPU 更直观因为“内存快满了”这句话非常容易理解。但真到了 Linux 上反而更容易因为“看起来很满”而误判。原因很简单Linux 会主动用空闲内存做缓存。也就是说“内存被用掉了”本身不是问题关键要看这部分内存到底是什么。3.1 第一眼不要盯着free先看availablefree-hcat/proc/meminfo|egrepMemTotal|MemFree|MemAvailable|Cached|SwapTotal|SwapFree|Dirty|Writeback|Slab|SReclaimable|SUnreclaim|Committed_AS|CommitLimitvmstat15psaux--sort-%mem|head-n10很多人第一次看free -h会本能地关注free那一列但线上排查时更有判断价值的是available。因为available更接近“在不明显触发交换的情况下还能给新进程用多少”。如果free很低但available还比较健康同时Cached很大那大概率只是页缓存正常占用了内存并不是系统真的快撑不住了。3.2 内存问题先分成四类看现象更像什么核心判断MemAvailable低si/so持续非零物理内存紧张已经开始换页性能通常会明显抖动VmRSS、RssAnon持续上涨用户态内存泄漏或对象堆积看趋势不看某个瞬时值Cached很大但available还可以正常页缓存不要误判为泄漏Slab、SUnreclaim异常上涨内核对象膨胀可能是 dentry、inode、socket 相关3.3 为什么内存问题要特别强调“趋势”因为单次截图很容易骗人。某个进程此刻VmRSS很大不代表它就有泄漏。可能只是它刚完成一轮批量处理内存还没回落也可能是它持有大量文件映射还可能只是运行期缓存比较多。真正值得警惕的是“业务高峰过去了它的 RSS 还是持续往上走而且回不来”。所以内存问题的判断最好带一点时间维度pidstat-r-pPID15cat/proc/PID/status|egrepVmSize|VmRSS|VmSwap|RssAnon|RssFile|RssShmempmap-xPID|tail-n20如果还不够再看grep-E^(Size|Rss|Pss|Private|Shared|Anonymous|AnonHugePages)/proc/PID/smaps|head-n803.4 用户态泄漏通常长什么样它往往不是“瞬间爆掉”而是慢慢涨上去。特征通常有这些VmRSS或RssAnon持续增长业务流量已经降下来内存却不回落MemAvailable越来越低如果已经开始换页si/so会变得明显对 C/C 程序来说这类问题经常和对象生命周期、容器无限增长、缓存淘汰策略失效、跨线程对象持有有关。对 Java、Go 之类运行时语言来说还要结合 GC 行为、堆上对象增长、逃逸、引用链去判断。3.5 页缓存大不是坏事这是 Linux 新手最容易被误导的地方。如果你看到Cached很大MemAvailable仍然不低业务没有明显换页进程 RSS 也不夸张那这通常不是问题而是内核在正常利用内存做文件缓存。缓存本来就是为了提升 IO 命中率存在的只有当它挤压到真正可用内存或者引发回收压力时才值得担心。3.6 内核内存异常怎么识别有时候用户态进程看起来都不算夸张但系统可用内存仍在明显下降。这时就要怀疑是不是内核对象在涨。可以先看slabtop-o如果Slab、SUnreclaim明显上涨slabtop里某类对象数量持续膨胀那问题就未必在业务堆内存本身而可能在 dentry、inode、socket buffer 之类的内核缓存。3.7 容器环境里宿主机“有内存”不代表容器能活这是现代线上环境里非常值得补的一层。如果你的服务跑在 cgroup v2 下容器本身的memory.max、memory.high、memory.current比宿主机总内存更关键。很多时候宿主机还没 OOM但容器因为超过自己的上限已经被强回收甚至被杀了。cat/sys/fs/cgroup/cg/memory.currentcat/sys/fs/cgroup/cg/memory.peakcat/sys/fs/cgroup/cg/memory.eventscat/sys/fs/cgroup/cg/memory.stat如果memory.events里的oom、oom_kill在增长那问题已经非常明确了。3.8 现代系统里别漏掉systemd-oomd不少人只会去dmesg里找 OOM 日志但如果机器启用了systemd-oomd进程可能在传统内核 OOM 之前就被主动清理掉。oomctl dumpdmesg-T|grep-Eioom|killed process|tail-n20这一步的价值在于弄清楚“到底是谁杀了它”。如果这一点都没分清后面的分析很容易走偏。3.9 内存方向最常见的四个误判看到MemFree很低就说机器内存不够。看到Cached很大就下结论说“内存泄漏”。只看宿主机总内存不看 cgroup 限额。用单次 RSS 截图判断泄漏而不看趋势。4. 磁盘 IO 排查先确认是不是排队再找谁在写磁盘 IO 问题是最容易“伪装成 CPU 问题”的。因为一旦块设备开始排队任务就会进入不可中断等待负载会上升CPU 面板上也会出现明显的wa。如果这时候只盯着 CPU 面板很容易误以为“CPU 告警就是 CPU 不够”实际上 CPU 只是在等磁盘。4.1 这一段第一组命令最好是vmstat15iostat-x-y15pidstat-d-pALL15cat/proc/meminfo|egrepDirty|Writeback这四个命令配在一起基本能把 IO 的大方向看清楚。vmstat里的b能看到有多少任务在阻塞。iostat -x能看到磁盘是不是在排队。pidstat -d用来找具体哪个进程在读写。Dirty和Writeback可以帮助判断页缓存回写压力。4.2iostat -x里最值得看的不是带宽而是延迟和队列很多人一看到rkB/s、wkB/s就开始紧张觉得带宽很大就一定是问题。其实不是。真正决定“业务为什么慢”的通常不是吞吐数字本身而是请求有没有在设备前排队。判断这件事最有价值的三列是await一次 IO 从入队到完成的平均时间aqu-sz平均队列长度%util设备忙碌时间占比这里尤其要强调两个判断点。第一await高而且aqu-sz高才说明“慢是因为排队”。第二%util很高并不永远等于“磁盘一定到极限了”特别是在现代 SSD、RAID 或更复杂的存储栈上不能机械地只拿这一列做结论。4.3 进程级 IO 定位为什么/proc/PID/io很关键系统级看到“磁盘忙”以后下一步一定要尽快把问题缩到进程层面。pidstat-d-pALL15cat/proc/PID/iopidstat -d可以快速找出哪个进程kB_wr/s或kB_rd/s异常高。而/proc/PID/io更进一步能帮你看清楚它发起了多少写系统调用它真正写到了多少字节有没有大量写调用其实只是碎小日志如果你发现syscw特别高但write_bytes并没有高到相同比例常常意味着写得很碎调用频率远高于有效写入量。这种场景在日志系统上非常常见。4.4fsync为什么经常是关键嫌疑人很多程序员以为“写文件慢”就是写得太多其实线上更常见的情况是写本身不算多但每次写完都在同步刷盘。这时可以用strace-f-tt-T-etracewrite,fsync,fdatasync-pPID如果你在输出里反复看到fsync()或fdatasync()那延迟高的原因就不只是“有写”而是“每次写都在等介质确认”。数据库、日志、消息持久化模块里这类问题尤其常见。4.5 页缓存回写压力也会把前台请求拖慢Linux 下大量写操作通常会先进页缓存再由内核异步刷盘。这种设计本来是为了吞吐但在写峰值特别高的时候也可能积压出大量待回写脏页最终反过来压到前台线程。所以如果你看到Dirty很高Writeback持续不低await也逐步抬高那就要考虑是不是页缓存和后台回写开始互相牵制了。4.6 磁盘 IO 方向最常见的三个根因生产环境日志级别过低每个请求都写大量 DEBUG 日志数据库或存储服务发生大量随机写或刷盘应用写得很碎还频繁做同步持久化4.7 IO 方向最常见的三个误判看到%util100%就直接断言“磁盘一定满了”。只看带宽不看await和aqu-sz。只停留在系统级不继续追到具体进程和具体调用。5. 网络排查不要只数连接还要看连接质量网络问题看起来最杂因为它既可能是链路问题也可能是应用本身的连接管理问题还可能是监听队列、socket 缓冲、DNS、对端处理能力共同作用的结果。所以网络排查不能只看“连接多不多”而要同时看三件事连接状态是否异常堆积。单条连接的 RTT、重传、拥塞状态是否异常。监听端口的接收能力和队列是否成了瓶颈。5.1 第一组命令ss-sss-tanss-tanstate established ss-tanstate time-wait ss-tanstate close-wait ss-tiss-mss-lnt5.2TIME-WAIT多不一定就是故障这是网络排查里最经典的误判之一。很多短连接服务天生就会产生大量TIME-WAIT这本身是 TCP 正常关闭流程的一部分。只有当它已经影响到端口资源、建连能力或者和业务设计明显不匹配时它才真正构成问题。相比之下CLOSE-WAIT持续增长往往更值得警惕。因为它通常意味着对端已经把连接关了而本端没有及时close()。这更像代码层面的连接清理问题。5.3 为什么ss -i特别重要很多人网络排查停在ss -s或netstat这一层只能看到连接数量却看不到连接质量。ss -i则能直接给出一些真正有判断价值的 TCP 内部信息比如rtt往返时延rto重传超时cwnd拥塞窗口ssthresh慢启动阈值如果一个接口超时但你用ss -i看到连接的rtt、rto都明显升高那就说明问题已经不是“连接有没有建立起来”这么简单而是链路质量或对端处理能力已经出了明显问题。5.4ss -m用来判断 socket 层是不是堆积了如果连接数量看起来不夸张但服务仍然发不动、收不动就应该去看 socket 内存和队列。ss -m里值得关注的包括rmem_allocwmem_allocwmem_queuedback_logsock_drop这些信息的意义在于它能帮助你判断问题是出在链路上还是应用根本没来得及消费和处理。5.5 监听端口的问题别只看“有没有在 LISTEN”一个端口在LISTEN不代表它就处理得过来。如果服务端accept()太慢或者用户态工作线程已经堆满监听队列同样会积压。外部表现往往是建连慢偶发连接失败高峰期抖动特别明显这时要一起看ss-lntcat/proc/sys/net/core/somaxconncat/proc/sys/net/ipv4/tcp_max_syn_backlog它们能帮助你判断 backlog 上限和实际监听能力是否匹配。5.6 网络方向最常见的几个根因…详情请参照古月居