8.3 线上排错与复盘方法论 本节摘要:故障处理分两幕:现场(止血优先、证据保护、假设验证)与事后(把根因写成制度)。本节给出从告警到根因的排错决策树、JVM 三板斧的取证清单、"五问法"根因分析,和一份可直接落地的事后复盘模板——让每个事故都变成系统的免疫力,而不是重复的学费。 第一幕:黄金十分钟的动作序列 支付服务 P99 告警触发。错误的时间线是:一群人挤进会议室七嘴八舌、有人开始翻代码猜原因、有人手忙脚乱重启把现场毁了。正确的时间线是三条并行的动作流: 止血流(第一优先级,与诊断并行):"最近十分钟内变更过什么?"——发布、配置、开关、流量,八成的故障与变更相关(第 7.2 节的 SMS SDK、第 5.2 节的新版本死锁都是变更引入)。有变更先回滚,回滚不需要理解根因;
本节摘要:故障处理分两幕:现场(止血优先、证据保护、假设验证)与事后(把根因写成制度)。本节给出从告警到根因的排错决策树、JVM 三板斧的取证清单、"五问法"根因分析,和一份可直接落地的事后复盘模板——让每个事故都变成系统的免疫力,而不是重复的学费。
支付服务 P99 告警触发。错误的时间线是:一群人挤进会议室七嘴八舌、有人开始翻代码猜原因、有人手忙脚乱重启把现场毁了。正确的时间线是三条并行的动作流:
止血流(第一优先级,与诊断并行):"最近十分钟内变更过什么?"——发布、配置、开关、流量,八成的故障与变更相关(第 7.2 节的 SMS SDK、第 5.2 节的新版本死锁都是变更引入)。有变更先回滚,回滚不需要理解根因;没有变更则降级(摘流量、关非核心功能、限流)。止血的目标是把影响面积按住,不是把问题查清——排查是第二幕的事。
取证流(重启/回滚前必做):证据是易腐品。三件套各花一分钟:
jstack <pid> > jstack-$(date +%H%M).txt # 线程状态 死锁 阻塞点 jmap -dump:live,format=b,file=heap.hprof <pid> # 堆现场 第6.1节的案发照片 jstat -gcutil <pid> 1000 > gc-snap.txt # GC 快照
加上:最近一段 GC 日志的归档、数据库慢日志与进程监控截图、告警时间轴导出。重启销毁的是堆与线程现场——没有 dump 的 OOM 复盘只能靠猜(第 6.1 节的教训,生产预配 HeapDumpOnOutOfMemoryError 就是为了这一刻)。
观测流:拉出时间轴对齐看——告警时刻、流量、错误率、GC、连接池、下游 RT、变更记录,画在一条线上。第 4 章"线程多 CPU 低"、第 6 章"P99 尖刺对 GC 事件"、第 7.3 章"连接等待超时"的判断,全部来自这种对齐。相关性先于因果:时间轴上同时动的两条线,就是最好的下钻入口。
证据在手后进入诊断循环:假设 → 最小代价验证 → 结论/新假设。"可能是缓存穿透"就去查缓存命中率曲线,而不是顺手把缓存 TTL 改了看看。每个假设配一个可证伪的观测动作,失败照样有信息量(排除了一个方向)。排错中最贵的状态是"漫游":没有假设地翻代码、凭印象改配置——第 8.2 节的翻车现场在故障里同样上演。
JVM 侧的取证清单按症状索引(本册各章的浓缩):
止血与修复完成,才是真正决定团队水平的环节。低质量复盘的标志:根因写"代码考虑不周",改进项写"加强测试、下次小心"——不可执行、不可验证、不可防止重犯。高质量复盘用五问法(对每个直接原因连问五次为什么)挖到系统性层:
现象:支付回调丢失三天 为什么1:异常被空 catch 吞了(直接原因 第3.1节) 为什么2:紧急上线时为了先恢复服务加了绕过 代码走查没发现 为什么3:代码评审对空 catch 没有硬性检查项 为什么4:评审清单本身没有"异常处理"条目 为什么5:故障驱动的经验从未沉淀为清单 团队每次从零学 系统性根因:缺防坑清单制度 改进项:建立评审清单含异常处理硬条款 CI 静态扫描空catch 告警
五问的方向要对着系统与流程问,不是对着人问——"他为什么犯错"的终点是追责,"什么系统让人容易犯这个错"的终点是改进。改进项的验收标准:可执行(有 owner 与截止日)、可验证(怎么算做完)、面向制度(工具、清单、流程,而非"细心一点")。
复盘模板(可直接搬进 wiki):
故障后的"机会窗口"稍纵即逝:痛感最强的一周内把清单与扫描规则落地,三个月后同样的痛就只剩财务数字了。这也是本册反复出现"防坑清单"的原因——每节的清单合起来,就是一份从 76 篇旧文档与几十次事故里蒸馏出来的 Java 生产防线。
⚠️ 常见坑:复盘变追责会。一旦会议的产出是"谁背锅",下一次故障的第一动作就从"报证据"变成"藏证据"——信息流通断了,团队免疫力的根基就断了。 blameless 不是不严肃,是把严肃用在系统上。
💡 关键直觉:事故是系统免费寄来的渗透测试报告。复盘中唯一真正的失败,是让同一份报告寄来第二次。
全书至此收束。回头看,从 Integer 缓存到复盘制度,一条主线贯穿:Java 的坑源于语言默认行为与生产规模的错配,而防线的本质是把每一次错配变成显式的、可检查的约定。愿你的下一个深夜告警,十分钟内就能在这册书里翻到那一页。