事故结束后最常见的结局,是写一份三页文档、领一个"加强监控"的改进项,然后等下一次同类事故。复盘的价值不在记述过程,而在它能不能改变下一次的行为:某个检查动作进了发布流程,某条告警被补上,某段代码再也写不出来。
下面拆七件事:定问题、建时间线、分层定根因、转译"人的失误"、设计并验收改进项、沉淀成机制、控制适用边界。
1. 必须回答的四个问题
| 问题 | 回答形式 | 不合格的回答 |
|---|---|---|
| 发生了什么 | 时间线 + 可观测证据 | "服务挂了半小时" |
| 为什么发生 | 机制链:A→B→C→现象 | "代码有 bug" |
| 为什么没早发现 | 检测链路断在哪一环 | "监控不够完善" |
| 怎么保证不再发生 | 可验收的改进项 | "加强意识" |
第三个问题最常被跳过。事故往往不是"没人发现",而是"信号存在但没接到人":有指标没告警、有告警没人看、有人看不认为是问题——每一环都要说清断在哪。
第四个问题的判据:**改进项能不能被一个没参与事故的人按步骤验证。**不能,就是口号。
四个问题的作答骨架(每行后面必须跟证据)
1. 发生:T0 <时刻> <现象>;证据 <指标/日志>
2. 原因:<变更> → <中间机制> → <现象>;证据 <慢日志/链路>
3. 未早发现:断在 <指标→告警 / 告警→人 / 人→判断>;证据 <告警记录>
4. 不再发生:<改进项 id>;验收 <注入什么故障,看什么信号>
2. 时间线以证据为准
记忆会重建因果:事后回想时,人会把"我注意到 X"自动排到"事故发生"之前。时间线必须锚在有时间戳的东西上。
| 证据源 | 能回答什么 | 精度 |
|---|---|---|
| 应用日志 | 第一条异常、重试起点 | 毫秒–秒 |
| 监控指标 | 何时偏离基线、何时恢复 | 采集间隔(常 15s–1min) |
| 链路追踪 | 哪个调用先变慢 | 请求级 |
| 发布/配置记录 | 变更时刻与内容 | 分钟 |
| 接入层日志 | 流量形态、错误率 | 秒 |
| 系统日志 | OOM、IO 错误、连接重置 | 毫秒 |
时间线模板(一行一个事件,只写有证据的)
T-30m 18:32 发布 v1.4.2(含新增查询) [发布记录]
T-12m 18:50 慢查询 0.2/s → 6/s [DB 慢日志聚合]
T-6m 18:56 连接池 active 触顶,wait 上升 [连接池指标]
T-2m 19:00 接口 P99 120ms → 4.2s [接入层指标]
T0 19:02 错误率 >5%,首次告警 [告警记录]
T+3m 19:05 值班确认,开始排查 [工单时间]
T+18m 19:20 回滚 v1.4.2,指标恢复 [发布记录 + 指标]
两条纪律:**每个事件标证据来源;无证据的推测单列一节,不混进时间线。**混进去之后,复盘会基于错误顺序推出错误因果。
典型情况:告警时间被当成故障起点,"检测延迟"就被算成 0。起点以指标偏离基线为准,不以告警时间为准。
采集证据的最小命令集:
journalctl -u APP --since "2026-11-09 18:30" --until "2026-11-09 19:30" -o short-precise > app.log
grep -nE "ERROR|Exception|timeout|refused" app.log | head -40 # 只取前 40 条,找第一条异常
3. 根因的三个层次
同一个事故能在三个层次描述,只有第三层值得投入改造:
| 层次 | 描述 | 例子 | 收益 |
|---|---|---|---|
| 直接原因 | 触发故障的动作 | 新增查询未走索引 | 同类下次还来 |
| 触发条件 | 让它变成事故的环境 | 高峰流量叠加连接池偏小 | 缓解,不解决机制 |
| 系统性缺陷 | 为何能进生产且没被拦住 | 无 SQL 评审、无慢查询卡点 | 拦住一类问题 |
判断停在第几层只需追问:**把直接原因修掉,同类事故还会不会换个形式发生?**会,就还在第一层。
缺陷形态不多,对照比空想快:缺卡点、缺检测、缺验证、缺约束(靠自觉的规则)、缺预案。
4. "人的失误"不是根因
写成"操作失误""责任心不足",等于宣布复盘结束。人一定会失误,系统的责任是让失误不致命。
| 复盘里的写法 | 转译成什么 |
|---|---|
| 误操作删了配置 | 为什么操作能直接生效?有无审批、双人、回滚 |
| 忘记加索引 | 为什么缺 SQL 评审或慢查询卡点 |
| 没注意到告警 | 为何没升级?信息过载还是无责任人 |
| 上线前没压测 | 检查清单为何没这项,谁验收 |
固定句式:**"为什么这个失误能造成这个后果?"**问三层,落点通常在权限、校验、可逆性、可见性之一。这四项能工程化,"更细心"不能。
5. 改进项的三种类型
改进项不是愿望清单,每一类都有可执行的验收方式:
| 类型 | 作用 | 验收标准 | 不合格写法 |
|---|---|---|---|
| 检测 | 更早发现 | 注入故障,告警 N 分钟内触发且指向正确对象 | "加强监控" |
| 防护 | 让问题进不来 | 构造该场景的变更,被流程或校验拦下 | "加强评审" |
| 恢复 | 更快恢复 | 演练一次,恢复时间可测 | "制定预案" |
三类要配比:只做检测是"发现更快,照样出事";只做防护是"出事仍手忙脚乱"。
验收必须是实验,不是状态描述。"告警已配置"是状态;"注入一条无索引查询,5 分钟内收到含实例名与 SQL 摘要的告警"是验收。
# 改进项模板:字段空着就是没想清楚
- id: IMP-014
title: 慢查询超阈值告警并附 SQL 摘要
type: detection # detection | prevention | recovery
root_cause_layer: system # direct | trigger | system
owner: <姓名>
due: 2026-12-05
verify:
method: 在预发注入一条无索引查询
expected: 5 分钟内告警,含实例名、SQL 摘要、执行时长
evidence: 告警截图 + 规则 diff 链接
rollback: 关规则开关,不影响业务
status: open # open | verifying | done | dropped
dropped 要保留:确实不值得做的写明理由,挂着会让整张表失去可信度。
6. 不可验证等于没提
| 字段 | 缺失后果 | 最低要求 |
|---|---|---|
| 负责人 | 互相等待 | 具体到一个人 |
| 截止时间 | 无限延后 | 有日期,冲突时显式取舍 |
| 验证方式 | 无法判断完成 | 一条可执行的实验步骤 |
| 证据 | 无法复核 | 链接、截图、diff |
| 状态 | 表变装饰 | 定期过一遍,超期重决策 |
执行上只需一个动作:**每月 15 分钟过会,逐条只问——做完没?验证过没?证据在哪?**无证据一律退回 open。
7. 复盘的两种失败
| 形态 | 表现 | 后果 | 反向信号 |
|---|---|---|---|
| 追责会 | 聚焦"谁做的",当事人不敢说话 | 信息被隐藏,下次暴露更晚 | 会上没人说"我当时以为…" |
| 走过场 | 模板填空,结论全是"加强" | 改进项无人执行 | 文档写完即归档 |
追责会多因复盘与绩效挂钩:一旦有人因说实话受影响,之后所有复盘都会失真。走过场多因模板太好填,对策是提格式门槛——每条结论后必须跟证据或可执行动作。
8. 沉淀成能自动生效的东西
"文档里的一段话"最没用,它依赖有人记得去读。有效沉淀是三种:
| 形式 | 生效方式 | 承载什么 |
|---|---|---|
| 检查清单 | 发布时逐项勾选 | 需人判断的前置条件 |
| 自动化规则 | CI/流水线拦截 | 可判定的硬约束 |
| 告警项 | 指标越界通知 | 需要尽早知道的偏离 |
## 发布前检查清单(每条都能追溯到一个事故)
- [ ] 新增/修改 SQL 走索引,EXPLAIN 附在 PR ← INC-2026-031
- [ ] 新增外部调用设超时与重试上限 ← INC-2026-018
- [ ] 变更可在 5 分钟内回滚(步骤已演练) ← INC-2026-024
- [ ] 评估过高峰流量下的连接数/线程数上限 ← INC-2026-031
- [ ] 有对应告警项且能指向具体实例 ← INC-2026-009
追溯不到编号的条目会被逐渐删掉,因为没人知道为什么要留着它。能被机器判定的别留给人勾,否则清单会膨胀到没人读。
9. 跨团队可见性
| 做法 | 效果 | 成本 |
|---|---|---|
| 文档放公共可搜处 | 事后可查,不主动触达 | 低 |
| 月度事故摘要 | 主动触达,易被忽略 | 低 |
| 15 分钟分享会 | 能传机制,可提问 | 中 |
| 结论进公共清单 | 直接影响他人流程 | 中 |
| 跨团队事故库 | 长期最有效 | 高,需维护 |
最小可行:每条结论都要能落成别人可直接用的形状——清单项、告警规则、代码检查。只有本团队能懂的表述,跨团队必然丢失。
10. 排错:复盘本身失效的六种情况
| 现象 | 原因 | 处理 |
|---|---|---|
| 结论只有"加强监控" | 停在直接原因层 | 追问三层"为什么它能造成后果" |
| 改进项永远进行中 | 缺负责人或截止时间 | 补字段,超期重决策 |
| 开成批斗会 | 与绩效挂钩 | 明确不用于评价个人并兑现 |
| 文档没人看 | 只有叙述没有结论 | 结论抽成清单/规则,正文留机制链 |
| 同类反复发生 | 只修直接原因 | 补系统性缺陷层,拦一类问题 |
| 时间线自相矛盾 | 混入记忆与推测 | 标证据源,推测单列 |
第一条要强调:**"加强监控"的毛病不是错,是不可验收。**改成"为 X 指标加阈值告警,注入一次故障验证 5 分钟内触发"即可执行。
11. 边界:不必走完整复盘的情况
| 场景 | 为什么 | 替代做法 |
|---|---|---|
| 影响极小且原因明确 | 成本高于收益 | 一句话记录 + 直接修 |
| 尚未止损 | 结论会被现场推翻 | 先恢复再复盘 |
| 纯外部依赖故障 | 无可改的系统缺陷 | 只复盘"容错与切换" |
| 已有多起同类在复盘 | 重复产出 | 合并一次,聚焦系统性缺陷 |
| 首次遇到该类问题 | 缺共同语言 | 先 30 分钟对齐再走流程 |
取舍一句话:复盘成本要与影响匹配;但小故障的累积信号要有专门的地方收集,不能因为单次影响小就全部丢弃。
12. 自测
- 四个问题里哪一个最常被跳过?它要回答什么?
- 时间线为什么必须以证据为准?"故障起点"以什么为基准?
- 根因三层次分别是什么?怎么判断自己停在了第一层?
- 为什么"人的失误"不能当根因?转译的固定句式是什么?
- 改进项三种类型的验收标准各是什么?为什么必须配比?
- 沉淀成"文档里的一段话"为什么无效?三种有效形式是什么?