
凌晨两点四十七分告警短信像闹钟一样把我从床上拽起来。线上支付接口成功率掉了三个百分点用户侧已经开始出现“下单失败”的反馈。我眯着眼睛打开电脑第一件事不是看日志是先问自己三个问题变化是从什么时候开始的影响范围有多大目前有哪些线索可以缩小嫌疑区间这套条件反射式的动作背后不是天赋而是一套线上排查工具箱与排查方法论的支撑。干了这么多年线上故障处置我越来越深刻地体会到绝大多数排查效率低下的问题不是工具不够多而是工具没有分层、思路没有章法。这篇文章是“线上排查工具箱与方法论”系列的第一章我会从为什么需要方法论讲起把常见工具按层级归类再给出可直接落地的四步排查流程最后聊聊那些文档里不会写、但实战中一定会遇到的坑。如果你是一个刚接手线上系统的新人或者正在为“出了问题到处乱翻、查了半天定位不到根因”而头疼这篇文章应该能帮你把排查从“拼感觉”变成“走流程”。1. 为什么排查总是靠“感觉”——线上故障的四个反常识特性很多人以为线上排查难是因为系统复杂、代码量大。这个说法只对了一半。真正让排查变难的是线上故障本身具备的四个特性它们直接决定了我们为什么要先讲方法论再讲工具。1.1 线上故障像“薛定谔的猫”现象与根因之间隔着三层窗户纸你看到一个报错不代表根因就在报错的地方。最常见的情况是数据库连接池被打满业务侧报的却是“上游接口超时”磁盘写满了业务侧表现是“图片上传失败”中间隔了链路追踪、超时重试、负载均衡好几层。线上排查大量时间花在“拆窗户纸”上而不是花在“修东西”上。更麻烦的是现象本身还会“漂移”。一个内存泄漏问题早期可能只表现为GC时间变长再往后变成CPU飙升最后才以OOM的形式爆发。你从哪个现象切入决定了你会走多远的弯路。所以我一直强调排查的第一步不是开工具而是定义清楚“当前看到的现象到底属于哪一层”。1.2 复现难、窗口短一次故障就是一场限时解谜本地开发环境最舒服的一点是代码报错了可以加日志、断点重跑一遍。线上环境没有这个条件——流量是不可控的数据是千万级用户实时产生的很多故障就像幽灵一样出现几十秒就恢复了等你连上跳板机它已经消失。在这种“限时解谜”的场景里谁能更快地锁定证据谁就能赢得先机。这也是为什么我一直强调排查工具箱里的东西必须形成肌肉记忆。如果连top、jstack、tcpdump这些基础命令都要现场查手册故障窗口早就关掉了。1.3 排查效率的本质是证据链的完整度而不是经验多少我以前带团队时观察到一个现象两个同样资历的工程师遇到同一个故障一个花了十分钟定位根因另一个花了四十分钟还在反复试探差别不在于谁更懂代码而在于谁采集的证据更结构化。老手会在第一时间把CPU、内存、磁盘、网络、日志、链路追踪六类数据全部留痕新手则像无头苍蝇一样到处乱点最后想复盘时发现关键现场早就被后续操作覆盖了。所以方法论的价值在于它把“靠临场发挥”变成了“按清单执行”。哪怕你对这套系统不熟只要证据链采集完整根因往往自己就会浮出水面。1.4 工具箱和方法论是两条腿缺一条都会摔跤只有方法论没有工具箱定位到问题只能干瞪眼没有工具去验证只有工具箱没有方法论就会变成“手里拿把锤子看什么都像钉子”抓着一根iostat就怀疑磁盘拉一个jstack就怀疑死锁全凭运气。工具箱提供的是“弹药”方法论提供的是“瞄准镜”两者必须配套使用。2. 线上排查工具箱的分层地图从硬件到业务请求每一层配什么武器我习惯把线上排查工具箱分成五层网络链路层、系统资源层、进程应用层、业务数据层、环境应急层。每一层解决特定类型的问题工具之间不能互相替代。这个分层思想非常重要它决定了你在拿到一个故障时第一脚应该踩在哪里。2.1 顶层设计用分层思想管理几十种工具为什么要分层因为线上系统本身就是一个分层架构。用户请求从浏览器出发经过DNS解析、负载均衡、网关、应用服务、数据库再返回结果任何一层出问题都会在某一层表现出症状。分层管理工具箱就是给每个症状都准备好了对应的检查清单。你可以把这一层想象成医院的急诊分诊台胸痛可能来自心脏、肺部、肌肉骨骼、消化系统不可能一个科室解决所有问题。线上排查工具箱也一样让“对的工具”出现在“对的问题”面前本身就是效率。2.2 网络链路层ping、telnet、nc、traceroute、mtr、tcpdump网络层是排查的第一道关卡也是问题最容易被误判的地方。很多人一上来就ping通了就说“网络没问题”这个习惯非常危险。ping测的是ICMP协议通不通不代表TCP端口通不通更不代表应用层通不通。我平时做网络层排查会按这个顺序来排查目的工具关键输出探测主机是否可达、延迟是否正常ping丢包率、RTT均值端口是否开放、服务是否监听telnet ip port或nc -zv ip port连接成功或失败数据包经过哪些节点、哪里丢包traceroute/mtr每一跳的延迟和丢包率抓包分析具体交互过程tcpdump数据包内容、TCP握手状态DNS解析是否正确dig/nslookup解析IP、CNAME记录本机到目标的路由是否正常ip route/route -n路由表项这里特别提醒mtr是traceroute的加强版它会持续探测每一跳的丢包率比traceroute一次性输出更能反映链路质量的波动。如果入口流量有规律地丢包可以用tcpdump抓包看是不是TCP重传率过高。很多人看到某个网络测试工具箱已经打包好了这些基础命令界面化点一点就能跑全套网络检查确实方便但我还是建议你把命令行版的动作搞清楚出了问题才知道每个参数在干什么界面化的工具适合出报告不适合深度定位。2.3 系统资源层top、vmstat、iostat、free、mpstat、dmesg系统资源层回答的问题是操作系统本身还健康吗CPU、内存、磁盘、文件句柄这四类资源任何一项耗尽都会以各种诡异的形式体现在业务层。CPU问题top看整体按1看每个核按P排序进程mpstat -P ALL 1看每个CPU核的使用率是否均匀。如果某个核跑满而其他核空闲要考虑是不是绑核或者单线程瓶颈。内存问题free -h看物理内存和Swap重点看available而不是freevmstat 1看si、so两个字段一旦持续大于0说明物理内存真的不够了系统正在疯狂换页。磁盘问题iostat -x 1看%util和await%util接近100%不一定就慢还得结合await和svctm的比值判断是否真的饱和df -h看剩余空间df -i看inode有些时候明明空间还有却写不进文件那就是inode用完了。内核报错dmesg -T看有没有OOM killer的痕迹、磁盘I/O错误、软死锁等信息。很多莫名其妙的“服务挂了”翻dmesg一眼就能找到原因。有一个非常经典的坑某个进程把日志文件删了但进程还持续握着文件句柄写入df看到空间充足实际上磁盘一直在被一个看不见的文件消耗。这种场景用lsof -nP | grep deleted可以快速找到“已删除但还被占用”的文件属于系统资源层最容易被忽略的排查点。2.4 进程应用层ps、strace、lsof、jstack、Arthas进程应用层关注的是某个具体进程内部发生了什么。Java系的开jstack拿线程栈看是不是有死锁或长时间卡顿C系的用pstack所有语言通用的手段是strace跟系统调用。strace -p PID附加到进程查看它在执行什么系统调用。如果进程“卡住”了strace通常能直接告诉你它卡在read、write还是connect上。jstack PID打印Java线程栈。重点搜BLOCKED、WAITING、RUNNABLE三种状态BLOCKED多了说明锁竞争激烈大量线程堆积在同一个锁上基本就是代码问题了。Arthas阿里开源的Java诊断工具我愿称之为Java排查界的“瑞士军刀”。不用重启应用就能看方法入参、返回值、异常还能直接改日志级别、查在线类信息。遇到“明明代码看起来没问题但线上就是不对”的场景watch命令往往能一锤定音。这里我还想强调一个理念进程应用层的工具必须配合线程数和连接数一起看。比如通过lsof -p PID | wc -l看文件句柄数通过ss -s看Socket连接状态很多“进程hang住”的真相其实是文件句柄耗尽或连接数打满。2.5 业务数据层链路追踪、日志检索、Metrics监控、慢SQL定位业务数据层解决的是“这个请求在业务上下文里到底经历了什么”的问题。现代分布式系统的排查几乎离不开三类系统链路追踪Trace像SkyWalking、Zipkin、Jaeger这类组件通过一个traceId把一次请求串起所有微服务调用。排查慢请求时第一件事就是拿着traceId去链路追踪系统里看时间都花在了哪个服务、哪个环节。日志检索LogELK、Loki这类日志平台把分散在各个机器上的日志聚合起来支持全文检索和结构化查询。排查线上问题日志是还原现场最重要的证据来源没有之一。指标监控MetricsPrometheus Grafana提供趋势数据CPU、内存、QPS、错误率、P99延迟这些指标能帮你判断“从什么时候开始变差”。慢SQL定位是业务数据层另一个高频场景。大多数数据库都有慢查询日志MySQL可以通过slow_query_log开启再配合mysqldumpslow工具汇总可以直接找出TOP N慢SQL。实战经验是慢SQL往往是数据库连接池被占满、业务接口超时的前置原因所以一旦出现大面积超时数据库方向的排查优先级要往前提。2.6 工具箱的“操作系统”思维单体工具集、启动盘与自定义脚本工具分层之后还有一个“用什么载体承载”的问题。很多人可能听说过硬件圈流行的图吧工具箱那是把各种硬件检测、烤机、跑分工具集成到一个工具集里装机之后跑一遍全套体检能快速判断硬件健康度。这个思路对线上排查同样适用。我见过不少运维老手会维护一个“自用工具箱目录”里面大概长这样~/tools/ ├── net/ │ ├── ping.sh │ ├── mtr.sh │ └── tcpdump_http.sh ├── sys/ │ ├── top_cpu.sh # 按CPU占用排序展示进程 │ ├── top_mem.sh # 按内存占用排序展示进程 │ └── disk_io.sh # 一键输出iostat关键指标 ├── jvm/ │ ├── thread_dump.sh # 自动执行jstack并按线程状态汇总 │ └── gc_log.sh # 提取GC日志关键指标 └── trace/ └── query_traceid.sh # 根据traceId查询完整调用链把高频命令脚本化、菜单化之后有两个明显好处一是故障时可以减少键盘输入错误二来发布给团队时新人上手成本就低很多。像微PE工具箱这样的维护型启动环境也值得一提——系统都起不来的时候用它做个PE盘进维护模式照样能掏出工具来备份数据、修复引导。线上环境参考这个思路可以在跳板机上预留一套独立于应用环境的诊断工具集即使主环境挂了也能应急。3. 从“现象”到“根因”的推理链四步排查流程工具箱准备得再齐全没有一套推理流程串联也只是散装零件。下面这套流程是我个人实践中沉淀下来的不敢说放之四海而皆准但至少帮我解决过大量疑难杂症。核心思想是把模糊的故障描述一步步转化为可验证的因果链。3.1 第一步定义问题把“系统很慢”转化成可量化指标模糊的问题是没法排查的。你说“系统很慢”慢是慢在首字节慢在接口响应慢在数据库查询慢在页面渲染不同位置的慢排查方向天差地别。拿到故障第一件事是把描述转成指标用户反馈“打不开页面”→ 确认是全部用户还是部分用户是全部页面还是特定资源耗时是多少错误率是多少。告警显示“接口成功率下降”→ 落到具体接口名、错误码分布、P50/P99延迟变化曲线。数据库告警“连接数打满”→ 确认打满的时间点、增长斜率、当前活跃连接与空闲连接的比例。这一步输出的是一张“故障画像”什么时间开始影响哪些接口指标怎么变持续了多久当前是否恢复。有了这张画像后边的排查才有方向。3.2 第二步缩小范围用排除法建立二分空间范围缩小的核心策略是“二分法”。假设一个请求从客户端到服务端要经过5个环节先判断问题出现在客户端侧还是服务端侧再判断是入口网关还是下游应用再判断是应用逻辑还是依赖资源一层层往下切每轮都砍掉一半可能性。一个非常典型的例子是支付超时先确认是不是网络问题——从服务器ping下游支付网关再nc -vz探测支付网关的端口通不通。网络没问题进入应用侧——看链路追踪里支付调用环节耗时多长超时发生在建立连接、发送请求还是等待响应。应用侧也没异常进依赖侧——确认支付网关的凭证是否过期是否被限流对方近期有没有接口变更。这个过程很像玩“猜数字”游戏每次问一个能把范围砍半的问题。最忌讳的是东看一个指标、西拉一份日志完全没有递进关系那样只会把时间耗在海量信息里。3.3 第三步提出假设用证据链而不是直觉来检验很多工程师在排查时会陷入“我觉得是XXX问题”的陷阱但问他要证据又拿不出来。我的要求是每个假设必须至少匹配一条独立证据否则不进入验证阶段。比如“我怀疑是Full GC导致的服务暂停”你需要同时找到GC日志里有Full GC记录且暂停时间明显增长暂停时间点与告警时间点吻合线程栈或监控里能看到GC线程活动与请求超时的时间重叠。三条证据全部指向同一个结论才是“立得住”的假设。单条证据往往有迷惑性——比如GC日志有Full GC但也许它只是碰巧和故障时间重叠真正的原因在连接池耗尽。3.4 第四步恢复并验证确认“变好”是因为“做对”而非“碰巧”恢复操作和验证操作同样需要方法论。线上紧急情况下允许“先止血再查因”但止血之后必须跟进验证不能看指标恢复了就认为万事大吉。验证方式有三种时序验证指标恢复的时间点与操作时间点严格对应如果操作前指标已经自行恢复则不能归因于本次操作。对照验证在灰度环境或单台机器上调整参数观察行为是否与预期一致。压测验证用一个可量化的压测场景去复现故障条件验证修复手段在阈值上是否真的有效。我遇到过太多次“重启之后指标恢复了以为解决了结果第二天半夜又报警”的情况。原因就是只做了止血没做根因分析。第四步不是可选项是把“临时恢复”升级成“永久修复”的关键一步。4. 一线排查实战中那些“没人写进文档”的坑工具和方法论说起来都是框架性的东西真正的血泪教训往往藏在细节里。这节我挑几个高频的坑展开讲每一个都是我或者团队同事真金白银换来的经验。4.1 时间压力下的“假装排查”故障发生时最危险的不是不会排查而是为了“显得在做事”而乱做。我见过有人打开终端每秒敲一次top盯着屏幕看了十分钟什么都没看出来也见过有人把日志搜了一遍又一遍翻来覆去找同一个关键词——这些都是“假装排查”。真正的排查节奏应该是采集证据 → 分析 → 假设 → 验证 → 再采集。如果连续两轮验证都没有推翻或确认任何假设就要停下来重新定义问题了。停手不是为了拖延而是为了避免用无效操作污染现场——这一点在应急场景下尤其重要你的每一次错误操作都可能破坏关键证据。4.2 告警风暴其实是信息盲区的镜像告警规则设置不当会在故障时爆发海量无效告警把真正有价值的信号淹没掉。某次线上故障我经历了同一个故障触发了十几个不同维度的告警页面被刷屏刷到根本看不清哪个是根因最后只能把告警全部静音手工盯着核心指标看。事后我总结了一个原则告警要分层越靠近根因的告警越应该高优先级。数据库连接池打满比接口P99延迟上涨更接近根因进程OOM比负载均衡转发异常更接近根因。设置告警时多问自己一句这条告警如果真的响了它能帮我直接锁定根因吗如果只能提供模糊的“系统可能有问题”信号就应该降级或者合并。4.3 现场保护重启之前先保留证据这是最容易被忽视、一旦错过就永远无法弥补的环节。很多故障的证据只存在于内存里重启一次就永远消失了。所以我的习惯是在任何恢复操作之前先做一次完整的“证据快照”。参考这个检查单系统层面top -b -n 1快照、vmstat 1 5记录、free -h、df -h、iostat -x 1 3进程层面进程对应的线程栈、内存Dump有条件的话、核心转储文件如果配置了应用层面当时的日志片段、错误码统计、当前请求量网络层面抓包文件或ss -tlnp连接状态快照这套快照做完可能只要两三分钟但它会保住整个事故的“案发现场”。后续无论是定位根因还是写复盘报告这些数据都是无可替代的原始依据。4.4 监控指标之间的“时间差陷阱”另一个坑是监控数据汇总延迟。不同监控系统采集频率不一样口径也不一样。比如Prometheus的irate函数计算的是瞬时速率适合观察突变rate函数计算的是窗口平均速率适合观察趋势。如果拿irate和rate直接对比数值上没有可比性容易得出错误结论。实战中经常遇到的情况是业务侧的P99延迟告警已经响了但基础设施监控上的CPU曲线还是正常的一看时间轴才发现CPU数据延迟了几分钟。如果在时间维度上不严谨很容易误判为“不是资源问题”从而在错误的方向上浪费大量时间。我的建议是看监控对比曲线时先确认时间轴是否对齐再对比变化幅度不要只看“有没有变化”就下结论。4.5 连接池、线程池、队列——三类“看不见的瓶颈”很多时候系统整体CPU、内存、磁盘都看起来很健康但业务就是慢。这种情况大概率不是资源不够而是某个池子或队列被占满了。数据库连接池满了新的数据库连接请求全在排队HTTP线程池满了新的请求进不来表现为“连接能建立但请求无响应”消息队列积压消费者消费不过来表现为“数据延迟”。这一类的排查一定要结合应用层的监控去看比如连接池活跃数、线程池队列长度、消息积压数量。实践中最有效的手段是看线程栈分布——线程栈里大量线程阻塞在同一把锁或者同一个等待状态就是池子被打满的直观证据。5. 把一次事故变成团队的资产复盘和解剖不能变成走流程排查完、系统恢复之后大部分人会觉得“事情结束了”。但就我个人的体会事故的收尾工作才是价值最大的部分——一次事故如果只带来了一晚上的急救没有沉淀成任何东西下一次大概率还会以另一种姿势坑你一遍。5.1 复盘不是追责把“谁做错了”换成“系统哪里设计不足”复盘会上最没用的讨论就是“当时为什么不XXX”。人在紧急情况下的决策质量本来就会下降这不是靠事后批评就能提升的。我更关注的是另一组问题监控系统为什么没能更早发现是采集盲区还是阈值设得不对应急预案为什么不够顺手是不是因为平时没演练过根因链条上哪一环的防御是缺失的代码层、架构层、运维层分别能做什么改进把“人”从问题里拿掉只看“系统”层面的不足复盘才会产生真正的改进项而不是制造焦虑和推诿。5.2 排查文档的模板怎么设计才不流于形式很多团队的排查文档到最后只剩一个标题因为写文档本身就是个苦差事。我的做法是把文档模板设计成“填空题”每个参与排查的人只需要把证据链填进去就行不需要组织语言。推荐一个我常用的结构项目内容故障时间线分钟级记录什么时候出现异常、什么时候告警、什么时候止血、什么时候恢复关键指标变化附上对应时间点的指标截图或数值证据链日志、线程栈、抓包、监控曲线等原始证据的索引根因分析结论以及得出该结论的证据链影响面与恢复过程影响范围、恢复手段、操作人、操作时间改进项按P0/P1/P2分级明确责任人和截止日期用这种“证据填空”的形式文档产出效率会高很多复盘时对着时间线一条条核对毫无压力。5.3 工具箱的“差什么补什么”每次事故后补充工具工具箱不是一次性建好的它是跟着事故一起成长的。每处理完一个故障我会问团队一个问题“这次的排查过程中有哪些动作让人工重复测量了很多遍哪些命令是现场临时百度才找到的把它们沉淀成脚本。”比如某次排查发现故障时需要批量从几十台机器拉取GC日志来对比Full GC频率靠人肉一个一个机器敲命令太慢了。后来我写了个一键脚本遍历所有机器拉取GC日志并汇总关键信息几十秒出结果。这类小型工具并不起眼但积累多了就是一套非常有战斗力的团队排查工具箱。5.4 日常演练把“灾难”变成条件反射最后一个建议是演练。就像消防演习不是为了真的着火而是为了让动作变成肌肉记忆线上故障的处置也需要演练。团队可以定期把一个曾经真实发生过的重大故障拿出来做一次“桌面推演”不看答案所有人按实际排查流程走一遍限时一小时看大家能否通过证据链重新定位到根因。演练完之后把发现的卡点继续改进行动里比如哪些工具找不到、哪些权限没开、哪些入口不清这些平时根本不会注意的细节往往会在真实故障时给你狠狠上一课。经过两三轮这样的演练团队的线上排查能力会有质的提升。工具箱里的每一样东西都不会自动生效方法论也是。真正让它们发挥价值的是一次又一次刻意练习把工具用熟把流程走顺直到“按证据链排查”成为你面对告警时的条件反射。到那时候线上故障再突然你也不会慌。