故障排查流程
这一课你将能做到
- 遵循 五阶段排障流程(确认 → 止血 → 定位 → 修复 → 复盘),能在判题接口 5xx 告警时 30 秒内打开正确面板
- 在 Grafana RED 面板、Prometheus 指标、日志、链路之间建立高效跳转关系,说清「哪条信号先查、哪条后查」
- 写一份包含时间线、影响面、根因、行动项的 故障复盘报告(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 连接池)。
优先级:
1. 扩容 MySQL 连接池(立即生效)
2. 开启降级:返回静态提示「判题队列繁忙,请稍后重试」
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 万行
根因:新发布引入了缺失索引的查询 + 连接池配置未调整。
1. 立即:加数据库索引(CREATE INDEX ...)
2. 短期:连接池配置恢复合理值(max-active=50)
3. 长期:SQL 审查流程中加入 explain 检查
步骤 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
步骤 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,验证:
- 切换后 RED 面板的 5xx 率是否下降(改为正常响应,只是非最终状态)
- 用户侧是否收到友好提示而非 500 错误页
讲清楚要点
- 确认先于定位,止血先于修复——不要在还在出血的时候慢慢找根因,先让系统活着。
- RED 面板是确认的锚点——任何告警先打开 RED,30 秒内回答「用户是否受伤」。
- Trace 是因果线,日志是详情页,指标是聚合面——三者互补:trace 告诉你「哪一跳慢」,日志告诉你「为什么慢」,指标告诉你「有多大面积」。
- Postmortem 要 blameless——问「系统为什么允许这个错误发生」,而不是「谁犯了错」。人都会犯错,系统要防住。
- 行动项必须有 owner 和 deadline——没有责任人和截止日期的行动项等于没写。
- 复盘不是惩罚,是改进系统的机会——如果复盘后有人被追责,下次故障会被隐瞒。
- 演练比文档更重要——降级开关、回滚流程、面板跳转,每季度演练一次,确保 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 + 日志 + 指标,三层交叉验证 |
| 演练只读文档不操作 | 真故障时手忙脚乱 | 每季度演练降级、回滚、面板跳转 |
卡点急救
- Grafana 面板显示异常但找不到 traceId:检查日志 MDC 是否正确配置了 traceId;检查 OTel agent 是否对当前请求路径生效;检查 Jaeger 的时间范围是否覆盖故障时段。
- 定位到慢 SQL 但无法立即加索引:先止血(调大连接池 + 降级),同时联系 DBA 评估索引影响;如果表太大,考虑 online DDL 工具(如 pt-online-schema-change)避免锁表。
- 回滚后问题仍未恢复:说明根因不是本次发布。排查方向:基础设施变更(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,防止再发)、做得好的和待改进(团队学习)。关键原则:不追责、不掩盖、不敷衍——如果复盘后有人被批评,下次故障会被隐瞒,系统永远不会改进。
自测三问
- 判题接口 5xx 告警响起,你的前三个动作是什么?分别打开什么工具?
- 止血和修复的区别是什么?为什么止血优先?
- 写 postmortem 时,根因分析用 5 Whys,第五层通常问什么?
小结一句话
确认先于定位,止血先于修复,blameless 复盘防止再发——可观测信号是排查的地图,流程是地图上的路线。
延伸