CodeRoadMap
首页路线题库资源库文章社区
CodeRoadMap

程序员的学习成长路线图。内容免费开放,登录后可同步路线图与课时进度。

由 程序员柏风 独立维护 · 关于作者

学习

  • 路线图
  • 课程
  • 面试题
  • 文章
  • 我

更多

  • 资源
  • 社区
  • 关于作者
  • 内容说明

© 2026 CodeRoadMap

津ICP备2026012044号-1·津公网安备12011002026155号·coderoadmap@126.com
可观测性入门/课时/故障排查流程
RawBlame
课时目录12 节

可观测性入门

  1. 01可观测性三支柱
  2. 02指标与 SLI
  3. 03结构化日志
  4. 04分布式链路追踪
  5. 05插桩与埋点
  6. 06监控与告警
  7. 07遥测与 OpenTelemetry
  8. 08Spring 可观测性
  9. 09SLO 与错误预算
  10. 10仪表盘设计
  11. 11故障排查流程
  12. 12综合练习

故障排查流程

从告警到根因;对照 rel-postmortem。

30 分钟

故障排查流程

这一课你将能做到

  1. 遵循 五阶段排障流程(确认 → 止血 → 定位 → 修复 → 复盘),能在判题接口 5xx 告警时 30 秒内打开正确面板
  2. 在 Grafana RED 面板、Prometheus 指标、日志、链路之间建立高效跳转关系,说清「哪条信号先查、哪条后查」
  3. 写一份包含时间线、影响面、根因、行动项的 故障复盘报告(postmortem),能做到 blameless

为什么要学

告警响了,on-call 打开十几个工具手忙脚乱,30 分钟后才发现是 Redis 连接池配置错误——这是常见翻车场景。可观测性信号再全,没有排查流程约束,人会随机试错、遗漏线索、甚至误操作扩大故障。

判题系统的链路长:浏览器 → Nginx → Spring Boot → Kafka → worker → MySQL/Redis。任何一环出问题,症状可能表现在「提交接口 5xx」「判题结果延迟」「队列堆积」等不同地方。面试里「接到告警后你第一步做什么」「怎么快速定位根因」「复盘报告怎么写」是高频题,本课给流程框架。

先建立图像

阶段目标工具判题系统示例
确认判断是真故障还是误报Grafana RED 面板5xx 率 > 1% 持续 5 分钟?还是 Pod 滚动重启的瞬时抖动?
止血降低用户影响,不等根因降级开关、回滚、扩容开启降级返回静态提示;回滚可疑发布
定位找到根因所在层Trace → 日志 → 指标Jaeger 查慢 trace → Loki 查具体 submission 日志
修复解决根因代码修复、配置调整修复 Redis 连接池大小;调整 Kafka consumer 线程数
复盘防止再发Postmortem写时间线、影响面、根因、行动项
值班排查路径(判题接口 5xx 告警):

1. Grafana RED 面板(确认影响面)
   ├─ 5xx 率上升?确认时间点和持续时间
   ├─ 同时 QPS 下降?确认是流量问题还是服务问题
   └─ 最近发布?回滚候选

2. Grafana 依赖面板(缩小范围)
   ├─ MySQL 连接池:是否满?等待线程增加?
   ├─ Kafka lag:是否堆积?worker 是否消费变慢?
   └─ Redis 超时:命中率是否突降?

3. Jaeger 慢 Trace(定位具体请求)
   ├─ 找到 P99 突增对应的 traceId
   └─ 看哪一 span 耗时最长:JDBC?Redis?Kafka?

4. Loki 日志(验证根因)
   ├─ traceId = xxx 的日志
   └─ 异常栈:ConnectionPoolTimeout?SerializerException?

5. 止血与修复
   ├─ 回滚/扩容/降级
   └─ 验证面板恢复

跟着做

步骤 1:模拟一次故障并走完整流程

故障注入:把 MySQL 连接池 max-active 临时调低到 5,模拟高并发下连接等待超时。

阶段 1:确认(30 秒内)

打开 Grafana → 判题 RED 面板:

Row 1 RED:
  - QPS: 正常(说明请求进来了)
  - 5xx 率: 15%(确认:不是抖动,持续上升)
  - P99 延迟: 8 秒(确认:用户严重超时)

Row 2 SLO:
  - 可用率:从 99.95% 跌到 98%
  - 错误预算:今天已烧 30%

Row 3 依赖:
  - MySQL 连接池:活跃连接 5/5,等待线程 40+(关键线索!)
  - Kafka lag: 正常
  - Redis: 正常

结论:不是代码 bug(QPS 正常),是依赖瓶颈(MySQL 连接池)。

阶段 2:止血(1 分钟内)

优先级:
1. 扩容 MySQL 连接池(立即生效)
2. 开启降级:返回静态提示「判题队列繁忙,请稍后重试」
3. 准备回滚:检查最近发布是否改了连接池配置

注意:止血不等于修复。连接池调大可能掩盖真正的慢查询,后续仍需定位。

阶段 3:定位(3 分钟内)

1. Jaeger 查询:过滤总耗时 > 5s 的 trace
   → 发现 JDBC span "select submission by id" 耗时 4.5s

2. Loki 查询:traceId=xxx
   → 日志显示:"HikariPool-1 - Thread starvation or clock leap detected"
   → 同时出现慢 SQL:SELECT * FROM submissions WHERE id = ? (全表扫描嫌疑)

3. MySQL slow query log:
   → 确认该查询未走索引,单次扫描 200 万行

根因:新发布引入了缺失索引的查询 + 连接池配置未调整。

阶段 4:修复(止血后)

1. 立即:加数据库索引(CREATE INDEX ...)
2. 短期:连接池配置恢复合理值(max-active=50)
3. 长期:SQL 审查流程中加入 explain 检查

阶段 5:复盘(24 小时内)

见步骤 2。

步骤 2:写一份 postmortem 报告模板

# Postmortem:2026-10-07 判题接口 5xx 上升

## 时间线(精确到分钟)
- 14:00 发布 v2.3.1(含新查询逻辑)
- 14:15 Grafana 5xx 率突增至 15%(持续 5 分钟触发告警)
- 14:16 on-call 确认 RED 面板异常
- 14:17 扩容连接池,5xx 率降至 5%
- 14:20 开启降级返回提示页
- 14:25 Jaeger 定位到慢 SQL;Loki 确认索引缺失
- 14:30 加索引,服务恢复
- 14:35 关闭降级

## 影响面
- 持续时间:20 分钟
- 受影响请求:约 3000 次提交(5xx 或超时)
- 受影响用户:约 200 人
- 是否影响比赛:否(非比赛窗口)

## 根因(5 Whys)
1. 为什么 5xx?→ MySQL 连接池满,请求超时
2. 为什么连接池满?→ 新查询未走索引,单条 SQL 4.5s,连接长期占用
3. 为什么没走索引?→ 发布未经过 explain 检查
4. 为什么没检查?→ SQL review checklist 缺失「explain 验证」项
5. 为什么 checklist 缺失?→ 此前未将慢查询纳入发布门禁

## 行动项(含 owner 和 deadline)
1. [backend-team] 为 submissions 表加复合索引(已完成)
2. [dba-team] 发布流程中加入 explain 检查(deadline: 10-14)
3. [sre-team] 连接池告警阈值调低,提前预警(deadline: 10-10)
4. [backend-team] 慢 SQL 自动巡检脚本(deadline: 10-21)

## 做得好的
- on-call 30 秒内打开正确面板
- 止血动作(扩容 + 降级)在 3 分钟内完成

## 待改进
- 发布门禁未覆盖 SQL 性能
- 连接池告警阈值过高(当前 90%,应调到 70%)

步骤 3:建立「信号优先级」直觉

收到告警后,查信号的顺序:

1. Grafana RED(确认用户是否受伤)
2. Grafana 依赖/SLO(缩小范围:哪层出问题、预算烧了多少)
3. Jaeger 慢 Trace(定位具体请求和慢 span)
4. Loki 日志(验证根因:异常栈、业务上下文)
5. Prometheus 原始指标(验证假设:rate、histogram、基数)

反模式:收到告警直接 SSH 登录服务器看日志——慢、视野窄、错过全局信号。

步骤 4:验证 traceId 贯穿全链路

确认从浏览器到 worker 的 traceId 连续:

# Nginx access log
grep "traceId=4bf92f3577b34da6a3ce929d0e0e4736" /var/log/nginx/access.log

# Backend log
grep "traceId=4bf92f3577b34da6a3ce929d0e0e4736" /app/logs/backend.log

# Worker log
grep "traceId=4bf92f3577b34da6a3ce929d0e0e4736" /app/logs/worker.log

如果任何一环缺失,链路断在哪,排查就断在哪。修复方向见 obs-instrumentation。

步骤 5:演练一次「降级开关」

@Component
public class JudgeCircuitBreaker {
    @Value("${feature.judge.degrade.enabled:false}")
    private boolean degradeEnabled;

    public SubmissionDto submit(SubmitRequest req) {
        if (degradeEnabled) {
            return SubmissionDto.builder()
                .status("QUEUED")
                .message("判题队列繁忙,请稍后刷新结果")
                .build();
        }
        return doSubmit(req);
    }
}

通过配置中心(如 Nacos/Apollo)实时切换 feature.judge.degrade.enabled,验证:

  1. 切换后 RED 面板的 5xx 率是否下降(改为正常响应,只是非最终状态)
  2. 用户侧是否收到友好提示而非 500 错误页

讲清楚要点

  1. 确认先于定位,止血先于修复——不要在还在出血的时候慢慢找根因,先让系统活着。
  2. RED 面板是确认的锚点——任何告警先打开 RED,30 秒内回答「用户是否受伤」。
  3. Trace 是因果线,日志是详情页,指标是聚合面——三者互补:trace 告诉你「哪一跳慢」,日志告诉你「为什么慢」,指标告诉你「有多大面积」。
  4. Postmortem 要 blameless——问「系统为什么允许这个错误发生」,而不是「谁犯了错」。人都会犯错,系统要防住。
  5. 行动项必须有 owner 和 deadline——没有责任人和截止日期的行动项等于没写。
  6. 复盘不是惩罚,是改进系统的机会——如果复盘后有人被追责,下次故障会被隐瞒。
  7. 演练比文档更重要——降级开关、回滚流程、面板跳转,每季度演练一次,确保 on-call 真的会操作。

你可能会懵的地方

  • 先止血还是先定位? 止血优先。止血是降低用户影响(回滚、降级、扩容),不需要知道根因。定位是找根因,需要时间。顺序:确认 → 止血 → 定位 → 修复 → 复盘。止血时可以做初步定位,但不要等定位完才止血。

  • Trace 和日志怎么选? 有 traceId 时先看 trace(因果链清晰),再查对应日志(异常栈和业务字段)。没有 traceId 时(如批处理或基础设施问题),直接查日志和指标。不要只查一个——trace 告诉你「哪一跳」,日志告诉你「为什么」。

  • Postmortem 要写多长? 一页 A4 纸足够。核心:时间线、影响面、根因(5 Whys)、行动项(owner + deadline)。不要写成流水账,不要堆砌技术细节——细节附在附录里。

  • 和 reliability-essentials 的 postmortem 重复吗? 那门课讲复盘文化和组织流程;本课讲如何用可观测信号(RED → Trace → 日志)支撑排查过程。两门课交叉引用。

错法对照

错法问题正解
收到告警直接 SSH 看日志慢、视野窄、错过全局信号先 Grafana RED → 再逐层下钻
止血前等根因定位用户持续受伤先降级/回滚/扩容,再慢慢定位
复盘追责到人下次故障被隐瞒blameless,问系统为什么没防住
行动项无 owner/deadline等于没写每条行动项明确责任人和截止日期
只查日志不查 trace不知道哪一跳慢trace + 日志 + 指标,三层交叉验证
演练只读文档不操作真故障时手忙脚乱每季度演练降级、回滚、面板跳转

卡点急救

  1. Grafana 面板显示异常但找不到 traceId:检查日志 MDC 是否正确配置了 traceId;检查 OTel agent 是否对当前请求路径生效;检查 Jaeger 的时间范围是否覆盖故障时段。
  2. 定位到慢 SQL 但无法立即加索引:先止血(调大连接池 + 降级),同时联系 DBA 评估索引影响;如果表太大,考虑 online DDL 工具(如 pt-online-schema-change)避免锁表。
  3. 回滚后问题仍未恢复:说明根因不是本次发布。排查方向:基础设施变更(K8s 升级、网络策略)、依赖服务发布(MySQL 主从切换)、数据问题(坏数据导致循环查询)。

面试常问

  • 接到告警后你的排查流程是什么? 背这个版本:五阶段流程——确认 → 止血 → 定位 → 修复 → 复盘。确认阶段 30 秒内打开 Grafana RED 面板,判断是真故障还是误报;止血阶段 1 分钟内做降级/回滚/扩容,不让用户持续受伤;定位阶段用 Jaeger 查慢 trace、Loki 查日志、Prometheus 查指标,交叉验证找到根因;修复阶段解决根因并验证恢复;复盘阶段 24 小时内写 postmortem,含时间线、影响面、5 Whys 根因、行动项(owner + deadline),做到 blameless。

  • trace、日志、指标在排障中各扮演什么角色? 指标是聚合面,告诉你「有多少请求受影响、错误率多少、趋势如何」——用于确认影响面和范围。Trace 是因果线,告诉你「一次请求经过哪些服务、哪一跳最慢」——用于定位具体瓶颈。日志是详情页,告诉你「慢的那一跳出了什么异常、业务上下文是什么」——用于验证根因。三者缺一不可:没指标不知道多大面积,没 trace 不知道哪一跳,没日志不知道为什么。

  • 怎么写一份好的 postmortem? 核心结构:时间线(精确到分钟)、影响面(持续时间、受影响请求/用户)、根因(5 Whys 或鱼骨图,blameless)、行动项(含 owner 和 deadline,防止再发)、做得好的和待改进(团队学习)。关键原则:不追责、不掩盖、不敷衍——如果复盘后有人被批评,下次故障会被隐瞒,系统永远不会改进。

自测三问

  1. 判题接口 5xx 告警响起,你的前三个动作是什么?分别打开什么工具?
  2. 止血和修复的区别是什么?为什么止血优先?
  3. 写 postmortem 时,根因分析用 5 Whys,第五层通常问什么?

小结一句话

确认先于定位,止血先于修复,blameless 复盘防止再发——可观测信号是排查的地图,流程是地图上的路线。

延伸

  • Google SRE Book:Postmortem Culture
  • Atlassian Incident Handbook
  • Grafana Incident Response
  • 下一课《综合练习》:把 11 课内容串成一条完整的「判题系统可观测体系建设」路线。
加载本节练习…
← 上一课仪表盘设计下一课 →综合练习