2026/9/11 4:39:15

Linux服务器性能排查:CPU、内存、磁盘、网络全链路诊断指南

Linux服务器性能排查:CPU、内存、磁盘、网络全链路诊断指南 凌晨2点17分微信群里开始有人我“线上环境有点慢啊。”紧接着是第二条“首页接口超时了。”第三条还没发出来我就知道今晚的觉算是没了。这种场景对每一个手上管着Linux服务器的人来说都不陌生。做性能排查这件事难的不是命令不会敲最怕的是乱top敲一下、free看一眼、再ps列一堆进程琢磨半天最后还是说不清问题到底出在哪一屋子人陪着熬夜天亮复盘的时候讲不出所以然锅自然就落到运维头上。这篇指南想解决的就是这个痛点。我按生产环境真实事故的处理路径把Linux服务器性能排查拆成CPU、内存、磁盘、网络四条主线每条线上都给到具体命令、输出解读办法和判断标准再加上这些年实地踩坑总结出来的经验。无论你是刚接手服务器的后端开发还是专职运维、SRE或者只是偶尔在测试环境搭个服务这套方法都能直接拿来用。1. 先花三分钟定调比马上敲命令重要得多大多数人接到性能告警后的第一反应是立刻打开终端敲top看CPU。这个动作本身没毛病但如果你对故障范围、发生时间、关联变更一无所知top输出在你眼里就只是一串跳动的数字你很难快速判断哪个数字才是“凶手”。急救场景下效率就是一切。我强烈建议你花三分钟问清楚三个问题再决定往哪个方向查。1.1 每个性能事故先回答这三个问题第一个问题故障范围是什么是一台机器出问题还是整个集群都慢是所有接口都超时还是只有某个接口慢范围能帮你快速缩小排查边界。全集群都挂大概率是依赖的基础组件出了问题比如数据库、缓存、网关只有单台机器异样那基本就是这台机器的资源或进程出了问题。第二个问题从什么时候开始的是突然劣化还是慢慢劣化突然劣化通常意味着某个变更或者某个任务触发了质变比如定时任务启动、发布上线、日志暴涨慢慢劣化则更像资源泄漏、数据量增长、缓存命中率下降这类积累型问题。第三个问题最近有没有变更这里说的变更是广义的包括应用发布、配置修改、扩容缩容、内核参数调整、防火墙规则变动甚至机房网络割接。在我处理过的故障里大量问题的根因最后都指向变更。看起来这三个问题像废话但90%的排查弯路都是因为少了这一步。有一次同事跑来找我说服务器load高自己在上面top、ps查了半天没找到可疑进程。我问了他这三个问题之后才知道他是在运维平台上看到整个集群的负载曲线在上扬。范围搞错了方向自然就偏了——他一门心思在单机上找问题其实是某个网关节点在逐步把流量转发到集群里导致整体负载上升。1.2 变更记录往往才是“第一嫌疑人”“没有变更就没有事故”这句话在故障界流传了很多年虽然不能说绝对但它指出了一个事实系统在稳定运行状态下突然出问题背后几乎都有“变化”在推动。所以排查性能问题时一定要养成先翻变更记录的习惯。很多公司有发布平台、工单系统、配置管理库先查一遍最近几小时内的发布记录、配置改动记录。如果没有规范化的变更记录就找相关同事确认一下“你们最近动过什么”这比埋头看进程列表高效得多。举个真实案例。一次有个核心服务频繁超时数据库CPU飙升到接近满载DBA怀疑是慢SQL但抓了半天没抓到明显的全表扫描。最后查发布记录时发现半小时前另一个团队上线了一个新服务该服务的健康检查接口在代码里误写成了一个复杂查询每10秒执行一次直接把数据库连接池占满把核心服务拖垮了。那个新服务的进程负载其实极低但问题就是它引起的。这个案例给我的启发是性能排查不仅要看资源消耗水平还要看“谁在调用谁”。而变更记录是解锁这层调用关系最快的一把钥匙。1.3 把监控数据留存当成职业习惯急救时刻最怕什么最怕没有历史数据。当你看到当前CPU 100%却不知道它是一分钟前涨上来的还是已经持续了三小时你的判断依据就少了一大半。正规的监控系统Prometheus、Zabbix、云厂商监控等当然能提供这些曲线但很多中小团队并没有部署完整的监控。即便有也可能因为指标采集粒度太粗错过了关键细节。所以我个人的习惯是在重点服务器上常驻一个atop或者配置sar定时采集。atop可以按秒记录进程级别的CPU、内存、磁盘、网络使用情况放个几天不会占多少磁盘事后排查时用atop -r回放能精确到具体进程在什么时间占了多少资源。这招帮我翻过很多次案。如果你连atop都没装至少要在现场及时采集一份快照。我自己有一套“急救三联”命令出问题时先跑一遍再慢慢分析top -b -n 1 | head -50 free -h vmstat 1 5这几条命令能在几秒内给你一份当时系统状态的“初始证据”。后续如果系统自动恢复了这些输出就是复盘时最重要的素材。2. CPU篇load average不是“负载率”看错会误导整个排查方向CPU排查是性能急救里最常遇到、也最容易被误读的部分。很多教程上来就讲“load average不能超过CPU核数”这话有用但只说对了一半。理解load average的本质比背判断标准重要得多。2.1 load average的本质R状态和D状态进程的排队长度先看uptime的输出$ uptime 10:15:22 up 35 days, 2:31, 3 users, load average: 4.02, 3.85, 3.41三个数字分别是1分钟、5分钟、15分钟的平均负载。这里的单位不是百分比而是“处于可运行状态和不可中断睡眠状态的平均进程数”。R状态Running/Runnable正在CPU上执行或者排队等待CPU调度的进程。D状态Uninterruptible Sleep不可中断睡眠通常是等待磁盘IO、网络IO等内核态操作完成的进程。D状态进程不能被信号打断这也是它经常成为“甩锅重灾区”的原因。所以load average的含义说人话就是系统里有多少个进程“急着要干完活但还没干完”。如果你只有1个CPU核load average长期是1说明CPU刚好被占满超过1说明有进程在排队。但要注意load高不一定等于CPU忙。如果你发现load average很高但用top看CPU的us和时间都很低那多半是D状态进程在堆积也就是IO出了问题。这种“load高、CPU闲”的组合是磁盘或存储故障的典型信号。这时用vmstat 1看r列和b列能更清晰地分辨$ vmstat 1 5 procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu----- r b swpd free buff cache si so bi bo in cs us sy id wa st 3 1 51200 80240 1024 1048576 0 0 0 0 1200 3000 45 5 35 15 0r列是正在运行和等待CPU的进程数r持续大于CPU核数说明CPU调度不过来。b列是处于不可中断睡眠的进程数b列长期不为0重点查IO和存储。2.2 从top的us、sy、wa、st读出CPU的真实状态top输出的CPU行是每个排查人员都必须逐字段读懂的。典型输出如下%Cpu(s): 30.2 us, 8.5 sy, 0.0 ni, 55.0 id, 6.0 wa, 0.0 hi, 0.3 si, 0.0 stususer用户态CPU占用。高us说明应用程序自己在消耗CPU可能是计算密集也可能是代码里有死循环。sysystem内核态CPU占用。高sy说明程序频繁调用系统调用、内核锁竞争激烈或者网络/磁盘中断处理消耗高。waiowait等待IO完成的时间占比。wa高说明CPU在等磁盘/存储干活应用逻辑本身可能没问题。ststeal time虚拟机被宿主机“偷走”的CPU时间。云端ECS上如果st持续偏高那是宿主机资源争抢你需要考虑换规格或迁移。我一个很重要的经验是us和sy要分开看但更要合起来看。如果us和sy加起来都快100%了别急着说“CPU不够加机器”——先定位是哪个进程在消耗这比扩容更能解决问题尤其是线上应用代码出Bug导致死循环时加再多的机器也是白搭。2.3 一次Java进程CPU飙高的完整定位链top、pidstat、jstack假设你通过top看到某个Java进程CPU占用飙到了200%以上多核接下来怎么定位到代码级别我按步骤给你拆一遍。第一步确认进程PID。$ top -b -n 1 | grep java PID USER PR NI VIRT RES SHR S %CPU %MEM TIME COMMAND 5678 app 20 0 25.3g 1.2g 12m S 213.0 3.5 45:20.01 java第二步用pidstat按线程维度看CPU。$ pidstat -p 5678 -t 1 3找到CPU占用最高的线程TID。注意Java的线程ID和操作系统线程ID不是同一个东西需要把TID转成十六进制。$ printf %x\n 5678第三步用jstack抓线程栈在输出里找nid等于上面十六进制数的线程。$ jstack 5678 /tmp/jstack_5678.log然后在日志里搜nid0x……就能看到这个线程正在执行什么代码。十次里有八次你能直接定位到是哪个类、哪个方法、哪一行在空转。这套方法对Java服务百试百灵。其他语言也有对应工具比如Python可以结合py-spyGo可以用pprof思路都是一样的先到线程/协程维度再结合语言的运行时工具拿到调用栈。千万不要只在进程维度打转那样你只知道“这个进程很忙”却不知道它“为什么忙”。2.4 别忘了中断、上下文切换和绑核问题CPU排查还有一个常被忽略的角度上下文切换。用vmstat看cs列context switch和in列interrupt。如果cs数值巨大比如每秒几十万次即使CPU使用率不算特别高应用的响应时间也可能很差——因为CPU大量时间都花在切换进程/线程上下文上了真正干活的时长被挤占。什么情况会导致上下文切换高线程数过多的应用、锁竞争激烈的代码、大量短连接的网络服务。我遇到过几次诡异案例CPU使用率不高但接口响应就是慢最后发现是某些框架在每次请求时创建大量临时线程导致上下文切换像开机关枪一样频繁。另外多核CPU服务器上要留意NUMA架构和绑核问题。用taskset查看进程的CPU亲和性如果数据库或高频服务被绑定到了某个CPU的特定核上而这个核已经饱和性能就会明显受限。这种情况通常不靠“急救”解决但排查时如果发现CPU分布严重不均要往这个方向想一想。3. 内存篇free里的buff/cache不是“已用内存”别自己吓自己内存问题的排查第一步不是看Java堆或Golang的runtime而是先看操作系统层面的内存分配情况。但这恰恰是最容易误判的地方。3.1 available才是Linux给你算好的答案先看free的标准输出$ free -h total used free shared buff/cache available Mem: 15G 9.6G 1.2G 268M 4.6G 4.9G很多人看到used是9.6G心里就发毛觉得“内存用了快三分之二了是不是快不够了”。但请再仔细看free只有1.2G为什么available还有4.9G因为buff/cache这4.6G里有一部分是可以在应用需要时随时回收的。也就是说Linux会尽量把空闲物理内存拿来做缓存读缓存、页缓存、目录缓存等让文件访问更快。这会让人产生“内存用完了”的错觉但实际这些内存在应用申请时可以被内核回收使用。所以我的建议是判断内存够不够只看available这一列。available是内核估算出的“可用于启动新程序的内存大小”它已经扣除了不可回收的部分比used和free都更符合直觉。如果available长期只剩几百MB并且swap在持续增长那才需要真正警惕内存不足。3.2 swap的意义与“swap没了”的误区swap是内存的兜底。当物理内存不足时内核会把不常用的内存页换到磁盘上腾出物理内存给活跃进程。很多人有一个误区认为“swap用得越少越好”甚至有人直接把它理解成“性能下降的标志”。swap本身不是洪水猛兽它只是操作系统在内存压力下的一个缓冲机制。真正需要关注的是vmstat输出里的siswap in和soswap out这两列$ vmstat 1 5 procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu----- r b swpd free buff cache si so bi bo in cs us sy id wa st 2 0 51200 80240 1024 1048576 0 0 0 32 1200 3000 45 5 35 15 0如果si和so持续不为0说明系统正在频繁地换入换出内存页这时候应用会感觉明显的卡顿因为磁盘速度比内存慢好几个数量级。swpd这一列只是显示当前swap的使用量不为0不代表有问题关键看有没有持续的swap IO活动。另外sysctl里的vm.swappiness控制的是内核使用swap的“倾向程度”默认60。有人建议生产环境调成0或10这有一定道理但别指望调低swappiness就能避免OOM。它只是降低内核使用swap的优先级物理内存耗尽时该OOM还是会OOM。3.3 OOM Killer为什么Linux会选择“错杀”当物理内存真的耗尽时Linux内核的OOM Killer会被激活——简单说它得挑一个进程杀掉释放内存让系统活下去。问题是它并不总是杀掉“你最想让它杀”的进程。内核根据每个进程的oom_score打分这个分数综合考虑了进程占用的内存大小、运行时长、CPU使用量、优先级等因子。默认情况下内存占用越大、运行时间越短的进程越容易被选中。排查OOM的日志非常关键$ dmesg | grep -i oom $ journalctl -k | grep -i oom日志会清晰告诉你哪个进程因为内存不足被kill了当时系统内存总量多少、各进程的内存占用多少。这些信息就是你要找的“案发现场”。预防方面可以通过/proc/PID/oom_score_adj调整某个核心进程的OOM优先级。比如数据库、网关这类关键进程把oom_score_adj调低一点让内核在万不得已时优先杀别的进程。但这个方法只是降低风险不能根治内存不足核心还是要管好应用内存。3.4 定位内存大户ps、smem与泄漏判断定位谁在消耗内存最常用的是给ps按RSS排序$ ps aux --sort-rss | head -20注意RSSResident Set Size常驻内存集是一个进程实际驻留在物理内存中的大小但它不包含某些共享库被多个进程共享的部分。更严谨一点可以用smem看PSSProportional Set Size它会按进程对共享内存的占用比例进行分摊更接近真实占用。$ smem -rs pss | head -20判断内存泄漏关键要看趋势而不是瞬时值。找一个进程记录它现在的RSS过2小时再看如果进程在业务量不变甚至下降的情况下RSS还在稳步上涨那大概率有泄漏。很多中间件产品都出现过类似的“慢慢吃内存”的问题现象就是节点每隔几天或几周就被OOM掉重启后又能正常一段时间。关于内存还有一个经常被忽略的指标在/proc/meminfo里$ grep -E CommitLimit|Committed_AS /proc/meminfoCommitLimit是系统当前可以分配的内存总量上限Committed_AS是系统当前已承诺分配给进程的内存总量。如果Committed_AS长期高于CommitLimit说明内存超额使用严重未来一旦有进程申请更多内存OOM风险会显著上升。4. 磁盘篇df显示还有空间却写不进去这种“灵异事件”多半出在inode磁盘性能问题的表象很迷惑人。负载看着还行、CPU也不高、网络也没问题但服务就是卡或者干脆报错“No space left on device”——你df一看明明还有几十个GB可用。这种时候问题往往不在“空间”而在inode。4.1 df与df -i两条命令一起看文件系统要存一个文件不仅需要数据块还需要一个inode来记录文件的元数据权限、所有者、大小、数据块位置等。df看的是数据块占用df -i看的是inode占用。$ df -h $ df -i当你看到/dev/vda1的IUsed百分比到了100%即使容量还剩很多应用也一样会报“磁盘满”。因为inode耗尽了系统无法创建新文件连一个空文件都建不出来。这种问题最常见于什么场景大量小文件的目录比如临时目录、消息队列的持久化目录、邮件队列、Docker的overlay2层目录。我曾经在一台机器上排查过类似故障最后发现是某个日志组件把每个请求写成了一个小文件几天时间生成了一千万个文件直接把inode吃光了。清理思路有两个方向一是把大目录里已经没有价值的小文件批量删除二是如果业务确实需要海量小文件考虑改成对象存储、或者用大文件加索引的方式来存从根上解决inode压力。4.2 iostat -x 里的await、%util到底该怎么读当确认空间和inode都没问题时下一步就要看磁盘性能了。$ iostat -x 1 5重点关注这么几个指标%util设备带宽利用率表示设备处理IO请求的忙闲程度。但很多人忽略了%util达到100%不代表磁盘已经到极限它只是说明采样周期内设备一直处于忙碌状态。对现代SSD尤其是NVMe盘来说%util持续100%是常态磁盘依然能扛更高的吞吐。awaitIO请求的平均处理时间包含排队时间和实际处理时间。这个指标更贴近应用体感。如果await明显高于基线说明IO路径上存在瓶颈要么磁盘慢了要么请求排队了。rkB/s和wkB/s读写吞吐量。结合%util和await才能判断是“量太大”还是“单次请求慢”。一个可靠的判断方法如果%util高但await不高说明磁盘其实还能干活可能只是请求队列有点深问题不大如果%util不高但await很高那大概率是磁盘硬件本身有故障或者存储链路比如NFS、云盘延迟变大。这时的排查方向要转向存储后端而不是本地磁盘。4.3 用pidstat -d和iotop锁定IO消耗进程确认磁盘有性能瓶颈后下一步是找到“谁在制造IO”。首选是pidstat -d$ pidstat -d 1 5它直接列出每个进程的IO读写速率。这个命令的输出非常直观谁在大量读盘、谁在持续写盘一目了然而且不依赖额外安装工具。iotop也是好工具但需要root权限而且部分精简系统没装。优先级上我建议先掌握pidstat -d它属于sysstat包绝大多数Linux发行版都有。定位到进程后往下查它为什么读写。常见原因有日志框架没有异步写入每次请求都同步刷盘。数据库在做全表扫描或大范围排序临时文件写到了磁盘。备份任务在业务高峰期启动占满了IO带宽。搜索引擎或本地缓存服务在启动阶段做全量索引加载。如果是备份任务这类非核心作业导致的问题可以用ionice调整它的IO调度优先级把它放到后台“闲时”执行避免抢占核心业务的IO资源。不过这个命令需要内核IO调度器支持云服务器上未必有效更推荐的做法是把任务时间错开。4.4 “too many open files”与文件描述符排查服务突然报错“Too many open files”表面看是文件问题很多人会怀疑磁盘但实际这是文件描述符fd耗尽的典型症状。在Linux里一切皆文件。一个进程每打开一个文件、一个socket、一个连接都要消耗一个文件描述符。如果进程没有正确释放fd就会被慢慢耗尽。先看系统当前fd总使用量与上限$ cat /proc/sys/fs/file-nr第一个数字是已分配fd数第三个是系统总上限。再看进程维度$ ls /proc/PID/fd | wc -l或者统计所有进程的fd数排序找到大户$ for pid in $(ls /proc | grep -E ^[0-9]$); do echo $pid $(ls /proc/$pid/fd 2/dev/null | wc -l); done | sort -k2 -nr | head -10找到后用lsof查看这个进程具体打开了哪些文件$ lsof -p PID | head -50如果发现大量socket连接没关闭那问题又回到网络层连接泄漏如果大量日志文件被不断打开那要检查日志框架的滚动逻辑。修改fd上限临时生效用ulimit但要真正在服务重启后也生效需要在systemd服务文件里设置LimitNOFILE或者调整/etc/security/limits.conf。很多服务框架自带的“连接数上限”其实就受fd限制影响所以调这个参数常常能顺带解决“连接数上不去”的问题。4.5 日志爆量被忽略的/var/log磁盘排查里日志爆量是最常见也最好处理的一种但有一个细节经常让人栽跟头。先用du检查日志目录$ du -sh /var/log/* | sort -rh | head -10找到大文件后如果你只是rm删除了它但写日志的进程还在运行你会发现磁盘空间并没有释放。原因很简单进程拿着这个文件的fd文件虽然被删了但空间要等fd关闭才会释放。正确的清理方式是$ /var/log/xxx.log把文件内容截断而不是删除这样既能立即释放空间也不会影响正在写日志的进程。这是处理“磁盘空间明明删了却还是满”的关键一招。长期来看配置好logrotate或日志框架的滚动策略比每次手工清理要省心得多。给日志设置按大小或按天滚动并保留固定份数是每一台服务器都应该有的基础规范。5. 网络篇TIME_WAIT堆成山不一定有病端口耗尽才是真问题网络层的问题是性能排查里最容易被“表象”带偏的。比如你看到服务端存在大量TIME_WAIT状态的连接很多人的第一反应是“内核参数该调了”。但我要说TIME_WAIT多并不一定代表故障先搞清它为什么会存在再决定要不要处理。5.1 ss命令应该成为你的默认选择现在的Linux服务器上netstat虽然经典但ss更值得作为首选。它直接从内核的socket哈希表中读取数据速度快、信息全输出维度也更好用。$ ss -s Total: 1560 (kernel 0) TCP: 789 (estab 452, closed 260, orphaned 0, synrecv 0, timewait 180/0), ports 0 Transport Total IP IPv6 * 1560 - - RAW 0 0 0 UDP 17 14 3 TCP 789 743 46 INET 806 757 49 FRAG 0 0 0再看详细的连接状态分布$ ss -ant | awk {print $1} | sort | uniq -cESTAB当前建立的连接。SYN_SENT/SYN_RECVTCP三次握手进行中的连接。SYN_RECV大量堆积通常意味着服务端accept队列满了应用层处理不过来。TIME_WAIT主动关闭连接后本端进入的状态等待2MSL后消失。CLOSE_WAIT被动关闭连接后对端关闭了连接但本端还没关闭自己的socket。CLOSE_WAIT大量堆积是程序bug的标志。5.2 TIME_WAIT的前因后果与内核参数调整的适用边界TIME_WAIT不是故障它是TCP协议为了保证可靠性和防止旧连接数据串扰而设计的正常状态。主动关闭连接的那一端会进入TIME_WAIT默认等待60秒2MSL。如果服务器上TIME_WAIT很多首先要想的不是“调参数”而是“为什么有这么多主动关闭的连接”。常见原因应用代码里每次请求都新建短连接而不用连接池访问redis/mysql/HTTP上游时频繁建连、断连健康检查频率过高。如果你的场景确实无法避免大量短连接可以考虑内核参数优化。比如开启tcp_tw_reuse$ sysctl -w net.ipv4.tcp_tw_reuse1注意tcp_tw_reuse只对“发起连接的一方客户端”有效它让内核在新建连接时复用处于TIME_WAIT状态的连接。服务端场景这个参数帮不上忙。另一个参数tcp_tw_recycle在NAT环境下有很多隐患Linux新内核里已经移除或默认废弃不要在网上看到老教程就盲目开启。更彻底的解决办法是在应用层用连接池或长连接来减少频繁建连。治本永远比调内核参数可靠。一个更危险的场景是端口耗尽。当客户端机器频繁发起大量短连接时每个连接会占用一个本地端口默认范围是$ cat /proc/sys/net/ipv4/ip_local_port_range 32768 60999也就是大约28000个端口。如果TIME_WAIT中的端口数量接近这个范围新连接就会因为无法分配端口而失败。这时日志会报“Cannot assign requested address”流量会瞬时出现大量异常。快速验证方式$ ss -ant | grep TIME_WAIT | wc -l如果这个数接近28000端口耗尽基本就实锤了。临时可以把ip_local_port_range调大比如改成1024到65535但同样地长远要看应用层是否应该减少建连次数。5.3 连接数暴涨的定位从汇总到状态再到进程如果服务端连接数突然暴涨先从汇总看起$ ss -s然后按状态拆解是ESTAB暴涨还是SYN_RECV暴涨方向完全不同ESTAB暴涨可能是有热点事件导致流量激增也可能是连接泄漏。SYN_RECV暴涨服务端accept队列满了应用线程池被淹没多半是服务处理不过来。CLOSE_WAIT暴涨服务端代码没正确关闭socket属于应用bug。再往下按远程IP聚合看连接是从哪来的$ ss -ant | awk {print $5} | cut -d: -f1 | sort | uniq -c | sort -nr | head -10再用lsof把某个连接关联到具体进程$ lsof -i :8080 | head -20这套链路走下来基本能确认“谁在连谁、为什么连、连接状态是什么”。我之前查过一次事故服务的ESTAB连接数高得离谱CPU和内存却都很正常。按远程IP一聚合发现来源是同一个网段的几十台机器频率非常固定。最后查明是新上线的健康检查组件把检查间隔配置成了1秒相当于每台机器每秒钟都来建立一次新连接。调回30秒后连接数立刻恢复正常。5.4 带宽打满如何顺着流量找到源头系统资源都正常、CPU不忙、内存够、磁盘不慢但服务就是卡——这时候十有八九是带宽被堵了。带宽问题有个特点它不像CPU那样直接体现在进程列表上它更像一个“路上塞车”的问题所有流量都会受影响但让你看不出是谁造成的。定位带宽消耗先看网卡流量$ sar -n DEV 1 5或者用较新的$ nicstat 1 5看到某个网卡的rxkB/s或txkB/s接近带宽上限再进入下一步用iftop按连接或IP排序$ iftop -i eth0 -niftop能实时展示流量排名靠前的连接。配合lsof或ss就能看到是哪个进程、在跟哪个IP通信、占了多少带宽。如果是Nginx这类反向代理还要考虑到它的访问日志里做分析看是哪个域名或哪个URI引来的流量。有时候带宽打满不是流量真的涨了很多而是某个客户端在疯狂重试。比如某个下游服务超时后没有退避机制直接以极短间隔重试就会产生大量请求把上游的入口带宽打满。这种情况在应用层调节流控比单纯加带宽更有效。6. 拿到证据链之后怎么汇报才能“拒绝背锅”性能排查的最后一步不是关掉终端去睡觉而是把整个排查过程和结论整理成一份能拿得出手的汇报。这直接决定了你会不会背锅。很多人排查的时候挺能干但一到汇报就吃哑巴亏说不清楚时间线、拿不出指标数据、只能反复说“我查了挺久的”。这种事后的无力感比熬夜排查本身更让人难受。6.1 一套完整的事故证据链包含什么我自己整理证据时固定会收集四类信息缺一不可。第一是时间线。什么时候开始出现异常、什么时候被监控告警、什么时候做了第一次介入、什么时候恢复的。每一步都尽量精确到分钟并且标注与变更、发布的对应关系。时间线是最基础的证据也是评审会上最容易被追问的地方。第二是监控指标。CPU、内存、磁盘、网络这些核心指标的曲线截图或者命令输出。如果能在关键时间点给到atop或sar的历史回放数据那会非常加分。截图和输出都要保留原始格式别用语言转述代替真实数据。第三是变更记录。把故障前后所有的发布记录、配置变更、扩缩容操作列出来。这一步不是为了找责任人而是为了还原现场。很多时候变更记录能帮全组人一眼看出问题和变更的关联。第四是日志片段。应用日志、系统日志、内核日志里的关键报错截取带有时间戳的段落。这是判断根因的核心依据。日志尽量多截几行上下文单行报错经常断章取义。6.2 用一份“事故快报”让评审会回到事实汇报的时候建议用“事故快报”的格式要点是简洁、客观、有数据。大致分这么几个模块故障概述一句话说清楚影响范围和影响时长。时间线从异常出现到恢复的关键节点。现象与指标列出核心指标和对应的命令输出最好配合图表。根因分析用事实推导出结论区分“确定原因”和“待验证原因”。临时与长期措施当时怎么恢复的、接下来怎么防复发。责任人如果有指向事实而不是指向人。写快报的时候切记“用数据说话少用感受词”。不要写“感觉系统很卡”要写“接口P99延迟从50ms上升到5000ms”不要写“怀疑数据库慢”要写“慢查询日志显示xxSQL耗时从10ms涨到3000ms”。这套写法有一个额外的价值它逼着你自己把想法梳理清楚。很多时候我都是在写快报的过程中才意识到“真正的根因其实不是第一个怀疑的对象”。6.3 复盘根因、诱因与“运气差”的区别最后一步是复盘。这里最大的坑是把诱因当根因或者把两个混为一谈。举个例子。某服务OOM了诱因是流量增长表面根因是内存不够实际根因可能是应用层缓存没有设置上限或者某个接口把全表数据加载到内存里。如果你只定位到“流量涨了导致内存不够”这个复盘几乎没有任何价值因为真正的修复点根本没找到。验证根因的方式核心是“对照”。可以回滚验证回到变更前的版本看是否恢复、可以复现验证在测试环境构建相同条件看是否出现同样问题、也可以靠监控数据对照故障时段的指标是否与某个变更的动作在时间上高度吻合。复盘还有一个作用是沉淀“系统脆弱点”。每出一次事故都值得追问为什么这个故障没有被监控提前发现为什么加了那么多监控项覆盖不到这次的问题这些问题比单纯找责任人更有长期价值。我真正想说的是Linux服务器性能排查是个熟练工种也是一个良心活。命令你用熟了思路梳理顺了大部分故障都能在半小时内锁定方向。我和团队踩过的坑实在太多今天这篇更像是一个散装的手册希望你收藏下来真遇到事儿的时候能照着走一遍。最后再分享一个体会每次处理完一起事故花20分钟把过程写成内部记录这20分钟是特效药——长期下来你会发现自己对系统稳定性的判断力比绝大多数人都要准。