🏠 总目录📚 本教程 11 · 从告警到根因
📑 本页目录(点开跳转)

11 · 从告警到根因

42 分钟 | ⭐ 一份可以照抄的排查手册


🎯 一句话

线上排查最贵的不是修,是「不知道从哪查」。 这一章给一条固定的排查顺序 —— 从最便宜、最可能的原因开始,逐级往下。


🪜 一、排查顺序:便宜的先查

   ① 是真的吗?        —— 先确认指标本身没坏          5 分钟
   ② 是什么时候开始的? —— 定位时间点,圈定嫌疑范围     10 分钟 ⭐⭐
   ③ 是谁变了?        —— 我方发布 / 上游 / 外部       15 分钟
   ④ 是哪部分流量?     —— 分人群、分渠道拆开           15 分钟 ⭐
   ⑤ 是哪个特征?      —— 特征分布 + SHAP 对比         30 分钟
   ⑥ 是模型本身吗?     —— 最后才怀疑模型               —

🔑 ⑥ 放在最后不是随意的真正「模型自己变差了」的情况远比想象的少。 绝大多数线上问题是数据、管道、配置、外部变化造成的。 一上来就重训模型,是最常见的浪费。


① 是真的吗(别急着查)

   [ ] 指标口径最近改过吗?(埋点、SQL、看板定义)
   [ ] 数据是不是还没跑完?(离线任务延迟会让当天指标偏低)⭐
   [ ] 对比的基线选对了吗?(昨天是节假日吗)
   [ ] 分母变了吗?(比率类指标:分母涨了也会让比率跌)⭐

「分母变了」这条每年都会坑到人: CTR 跌了不一定是点击少了,可能是曝光暴涨(比如上了个新坑位)。 看比率类指标时,永远同时看分子和分母的绝对值。


② 什么时候开始的 ⭐⭐ —— 最有价值的一步

   把指标按【小时】画出来,找到拐点

   ⭐ 拐点的形状直接告诉你原因类型:

   突变(一小时内断崖)
     → 发布、配置变更、上游故障
     → 去查那个时间点前后一小时的所有变更  ⭐

   阶梯(某天开始换了个平台)
     → 上游数据源变了、口径变了

   缓慢下滑(几周)
     → 漂移(第 6 章),不是故障

   周期性(每天同一时间)
     → 定时任务、批处理窗口、跨时区流量

拐点时间是排查里信息量最大的一个数字。 有了它,你能把「所有可能原因」缩小到「那个时间点附近发生的事」—— 通常就剩三五件事可查。

配套要有的东西:一条变更时间线

   把这些放进同一张图(或同一个查询):
   · 模型/服务发布记录
   · 配置变更
   · 上游数据表的产出时间和行数
   · 运营活动排期
   · 外部事件(大促、节假日)

   ⭐ 这个「变更时间线」值得专门做一个看板,
      它能把 ③ 那一步从半小时压缩到一分钟

拐点其实可以让机器帮你找(不用肉眼盯曲线):

import numpy as np

def find_changepoint(series, min_seg=6):
    """最小二乘单拐点检测:把序列切成两段,找「组内方差和」最小的切点。
    series: 按小时(或按天)排好序的指标序列"""
    x = np.asarray(series, dtype=float)
    n = len(x)
    best, best_cost = None, np.inf
    for k in range(min_seg, n - min_seg):
        cost = x[:k].var() * k + x[k:].var() * (n - k)   # ⭐ 两段各自越"平"越好
        if cost < best_cost:
            best, best_cost = k, cost
    a, b = x[:best], x[best:]
    jump = b.mean() - a.mean()
    se = np.sqrt(a.var() / len(a) + b.var() / len(b)) + 1e-12
    # ⭐ 关键是这个 z:跳变幅度要和"本来的波动"比,否则天天都能找出拐点
    return {"拐点位置": best, "跳变幅度": jump, "显著性z": jump / se}

# 用法:把最近 14 天 × 24 小时的指标喂进去,拿到「第几个小时」

一定要看那个 z,不要只看 best: 这个函数永远会返回一个拐点,哪怕序列完全是白噪声。 经验阈值:|z| < 3 时基本可以认为「没有拐点,只是在正常波动」 —— 这时候你要查的不是「谁变了」,而是回到 ① 确认「跌」是不是真的存在。


③ 谁变了

嫌疑 怎么查
我方发布 发布记录对时间点;先回滚验证第 3 章)⭐
上游数据 表产出时间、行数、schema 变更记录
配置 阈值、截断长度、降级开关、AB 分流比例
外部 大促、竞品、节假日、舆情

一个关键原则:能回滚就先回滚,再慢慢查。 止损优先于定位。 回滚后指标恢复了,也顺便验证了「就是这次发布的问题」。


④ 哪部分流量 ⭐

   把指标按这几个维度拆开,看跌的是全体还是某一撮:

   · 新用户 / 老用户
   · 渠道(自然量 / 投放 / 各分发位)
   · 设备与版本(某个 App 版本?某个机型?)
   · 地域
   · 实验分组  ⭐

   ⭐ 如果只有某一撮跌 → 范围立刻缩小 90%
   ⭐ 如果全体均匀跌  → 多半是模型或全局配置

💡 拆开看还能发现第 14 章那种情况: 总体没变,但两个人群一涨一跌互相抵消 —— 那其实是两个问题。


⑤ 哪个特征(这一步用第 5、9、10 章的工具)

   ① 看特征监控看板:哪些特征的分布在拐点处变了(第 5、6 章)
   ② 按重要性排序,只看 Top-10(第 9 章)
   ③ 取一批「异常时段」样本和一批「正常时段」样本
      分别算 SHAP,对比每个特征的平均贡献  ⭐⭐(第 10 章)
      → 贡献变化最大的那个,通常就是元凶
import numpy as np, pandas as pd, shap

def diff_shap(model, X_bad, X_good, top=10):
    """对比异常时段和正常时段,找出贡献变化最大的特征"""
    ex = shap.TreeExplainer(model)
    sb = ex.shap_values(X_bad).mean(axis=0)     # ⭐ 带符号取均值,看方向
    sg = ex.shap_values(X_good).mean(axis=0)
    d = pd.DataFrame({
        "特征": X_bad.columns,
        "异常时段贡献": sb, "正常时段贡献": sg, "变化": sb - sg,
        "特征均值变化": X_bad.mean().values - X_good.mean().values,
    })
    return d.reindex(d["变化"].abs().sort_values(ascending=False).index).head(top)

同时看「贡献变化」和「特征均值变化」这两列,能直接分辨两种情况

贡献变了 特征均值变了 说明
输入变了(数据/管道问题)⭐ 最常见
输入没变但作用变了 → 可能是模型或编码问题

⑥ 最后才是模型本身

   走到这一步还没找到,才考虑:
   · 概念漂移(第 6 章)—— 需要有标签才能确认
   · 模型文件损坏 / 加载了错误版本(先看版本号!)
   · 训练/推理不一致(第 4 章)—— 跑一次逐字段比对

⚠️ 「加载了错误版本」比想象中常见,而且查起来只要 10 秒 —— 建议把它提到第 ① 步一起查。


💀 二、一次真实的排查复盘(完整时间线)

这个案例值得逐行读,因为它踩中了本教程前十章里的三个坑, 而且它的特征是「所有技术指标都正常」

   系统:反欺诈拦截模型,线上约 4000 QPS
   特征来自三处:
     · 请求参数        (实时,随请求带来)
     · 用户画像        (Redis)
     · 近 7 天行为聚合  (离线表,每天 04:00 产出)

告警:周二 09:12,拦截率从 1.8% 掉到 1.1%(告警阈值 1.5%)。

时刻 动作 看到了什么
09:15 ① 是真的吗 拦截数 7.2 万/天 → 4.4 万,请求量没变 → 不是分母问题;模型版本号 v213,与上周一致 → 不是发版
09:20 ② 什么时候开始 按小时画:周一 02:00 一小时内断崖 → 突变型 → 只查那一小时前后
09:25 ③ 谁变了 变更时间线看板上,02:00 没有我方发布;但 01:50 用户画像 Redis 集群做过一次扩容
09:40 顺着 Redis 查 扩容后新节点的 key 前缀配置写错一位 → 读不到 → 特征服务读超时 → 触发降级,返回默认值
09:50 ④ 哪部分流量 按「是否走了降级链路」拆开:降级组拦截率 0.3%,正常组 1.85% → 范围锁死
10:05 止损 回滚 Redis 配置;10:20 指标恢复

💀 这个故障最恶心的地方降级是设计好的行为,所以整条链路"完全健康" —— 接口成功率 100%,错误率 0P99 延迟反而从 42ms 降到 26ms (因为不查 Redis 了,更快了)。 一个只看「服务健康」的监控体系,对它是全盲的。

代价

   从周一 02:00 到周二 10:20 = 32 小时
   期间约 41% 的请求走了降级链路
   按事后回捞:漏放的欺诈交易约 4200 笔

   ⭐ 发现延迟 = 31 小时
      —— 而拦截率跌破阈值到有人处理,只花了 8 分钟
      真正的损失全在「发现」这一段

为什么没被早发现(三条,每条都对应前面某一章)

缺失 对应章节 补上之后
特征缺失率 / 默认值占比没进监控 第 5 章输入层 默认值占比 > 2% 就告警
降级链路没打点 第 17 章「走了哪条链路」 降级比例 > 5% 直接 P1
告警只挂在业务指标上 第 5 章三层监控 业务层是最慢的一层,输入层能提前十几小时

改进之后,三个月后同类问题(另一个特征源限流)再次发生发现延迟从 31 小时降到 4 分钟。 这就是第 8 章说的「每次事故都该让发现延迟下降一点」的实际样子。


🩺 三、症状 → 最先怀疑什么(速查)

这张表是上面那套流程的「快捷方式」:先看症状对号入座,猜错了再回到六步流程。

症状 最先怀疑 一分钟内怎么验证
指标断崖 + 成功率 100% + 延迟反而下降 降级链路被触发 看降级/兜底比例的打点
指标缓慢下滑数周 漂移,不是故障 看 PSI 曲线(第 6 章
只有某个 App 版本 / 机型跌 客户端埋点或协议变更 按版本号拆开
预测分数分布整体平移 ⭐ 某个特征量纲变了 看该特征的分位数,不是均值
新模型刚上线就差、离线却很好 训练/推理不一致 逐字段比对(第 4 章
每天固定时间跌一段 上游批任务延迟 看上游表的产出时间
总体跌,但每个人群都涨 💀 流量结构变了(辛普森) 看各组的样本量占比(第 14 章
恰好一半流量出问题 灰度批次 / 某个机房 / 某组实例 按实例 IP、机房拆
指标正常但方差突然变大 某个上游间歇性超时 看该依赖的 P99 和超时率

第一行是最值得记住的一条「所有技术指标都正常,业务指标却掉了」几乎总是降级、兜底或默认值。 因为真正的故障会留下错误率,而降级不会


📋 四、一份可以直接用的排查模板

## 事件:<指标> 于 <时间> 下降 <幅度>

### ① 确认
- [ ] 口径未变 / 数据已跑完 / 基线合理 / 分母正常
- [ ] 模型版本号符合预期

### ② 时间点
- 拐点:______(精确到小时)
- 形状:突变 / 阶梯 / 缓降 / 周期

### ③ 该时间点附近的变更
| 时间 | 变更 | 相关性 |
|---|---|---|

### ④ 影响范围
| 维度 | 受影响的分组 | 未受影响的分组 |
|---|---|---|

### ⑤ 特征层面
| 特征 | 贡献变化 | 均值变化 |
|---|---|---|

### 结论
- 根因:
- 止损动作 / 时间:
- **发现延迟**:______(从发生到发现)⭐
- 改进项:新增什么监控能提前 N 小时发现

最后两行是复盘的价值所在第 8 章): 每次事故都应该让「发现延迟」下降一点。


🧠 五、六个经验法则

   ① 止损优先于定位 —— 能回滚就先回滚  ⭐
   ② 一次只改一个变量 —— 否则恢复了也不知道是哪个起作用
   ③ 相信数据,不相信记忆 —— 「我记得没改过」经常是错的
   ④ 排查过程要边做边记 —— 否则第二天复盘时全忘了
   ⑤ 先缩小范围,再深入细节 —— 二分优于遍历  ⭐
   ⑥ 「没报错」不等于「没出错」 —— 降级、兜底、默认值都是静默的 ⭐⭐

💡 第 ② 条在紧急情况下最容易违反: 慌了就同时回滚模型、改配置、重启服务 —— 恢复了,但你永远不知道原因,下次还会再来一遍。

第 ⑤ 条的具体做法:排查像二分查找 —— 每一步都问「这个问题在哪一半」,而不是「是不是这个原因」。 六步流程本身就是一条二分链:时间维度二分 → 流量维度二分 → 特征维度二分一个维度平均能砍掉一半可能性,三个维度就是 1/8。


⚠️ 六、排查时最常踩的七个坑

后果 怎么修
一上来就重训模型 花两天训完,问题还在(根因在上游) 严格按六步走,模型永远放最后
同时改三件事 恢复了但不知道谁起的作用,下周原样再来 一次只改一个,每个改动写进时间线
只看总体曲线 「只有 iOS 某版本跌」这类问题永远看不到 告警自带按 4–5 个维度的自动下钻
拐点按「天」看 突变被看成缓降 → 误判成漂移 → 误决策去重训 排查时一律按小时
用「本周 vs 上周」当唯一基线 撞上节假日全是假告警,撞上大促全是漏报 同比 + 环比两条基线一起看
相信「我记得没改过」 90% 的「没改过」经不起查 只认变更记录,不认记忆
查完不写记录 三个月后同一个问题从头再查一遍 用下面的模板,「发现延迟」必须写

🔗 七、和站内其他章的关系

相关的地方 和这一章的关系
第 5 章三层监控 ⑤ 的数据来源
第 6 章漂移 ⑥ 的判断
第 9 章重要性 ⑤ 的排序依据
第 10 章 SHAP ⑤ 的对比工具
第 17 章链路打点 案例里最关键的那条日志
ML基础 11训练调试 那是训练时,这是线上
AI基础设施 23可观测性 基础设施层的同一套思路

✅ 检查点

  1. 排查的六个步骤是什么?为什么「模型本身」放最后?
  2. 「是真的吗」这一步要查哪四件事?哪一条每年都坑人?
  3. 为什么说拐点时间是信息量最大的数字?四种形状各指向什么?自动找拐点时为什么必须看 z 而不只看位置?
  4. 「变更时间线」该包含什么?它省了哪一步的时间?
  5. 排查时的第一原则是什么?
  6. 按流量维度拆开看,两种结果分别意味着什么?
  7. SHAP 对比时,同时看「贡献变化」和「特征均值变化」能分辨什么?
  8. 那个 Redis 扩容的案例里,为什么所有技术指标都正常?发现延迟是多少、后来降到了多少?「成功率 100%、延迟反而下降、但业务指标掉了」几乎总是什么原因?
  9. 紧急情况下最容易违反哪条经验法则?后果是什么?
👀 答案
  1. ①是真的吗 ②什么时候开始的 ③谁变了 ④哪部分流量 ⑤哪个特征 ⑥模型本身。放最后是因为真正"模型自己变差"的情况远比想象少,绝大多数是数据/管道/配置/外部变化——一上来就重训是最常见的浪费
  2. 口径改过吗、数据跑完了吗、基线选对了吗、分母变了吗「分母变了」每年坑人:CTR 跌可能是曝光暴涨(上了新坑位)不是点击少了。
  3. 因为有了它能把"所有可能原因"缩小到"那个时间点附近发生的事",通常只剩三五件可查。形状:突变→发布/配置/上游故障;阶梯→数据源或口径变了;缓降→漂移(不是故障);周期性→定时任务或跨时区。自动检测永远会返回一个拐点(哪怕输入是白噪声),所以必须看跳变幅度相对段内波动的 z 值;|z| 小于 3 时应当认为「只是正常波动」,这时要回到 ① 去确认「跌」是不是真的存在。
  4. 发布记录、配置变更、上游表产出时间和行数、运营排期、外部事件。它把第③步从半小时压缩到一分钟
  5. 止损优先于定位——能回滚就先回滚,回滚后恢复也顺便验证了是这次发布的问题。
  6. 只有某一撮跌 → 范围缩小 90%;全体均匀跌 → 多半是模型或全局配置
  7. 贡献变了 + 均值也变了 → 输入变了(数据/管道问题,最常见);贡献变了但均值没变 → 输入没变但作用变了,可能是模型或编码问题
  8. 因为触发的是"设计好的降级":Redis 扩容后 key 前缀配错 → 特征读超时 → 返回默认值。接口成功率 100%、错误率 0、P99 延迟反而从 42ms 降到 26ms(不查 Redis 更快了)。发现延迟 31 小时(跌破阈值到有人处理只用了 8 分钟,损失全在"发现"这一段);补上「默认值占比」监控和降级链路打点后,同类问题的发现延迟降到 4 分钟。所以「技术指标全正常、业务指标却掉了」几乎总是降级、兜底或默认值被触发——真正的故障会留下错误率,而降级不会,它是被设计出来的正常行为。
  9. 一次只改一个变量。慌了会同时回滚模型、改配置、重启服务 —— 恢复了但永远不知道原因,下次还会再来

🛑 可以停在这里

走神救援

排查顺序:便宜的先查——①是真的吗 ②什么时候开始的 ③谁变了 ④哪部分流量 ⑤哪个特征 ⑥模型本身。⭐⑥放最后是因为"模型自己变差"远比想象少,绝大多数是数据/管道/配置/外部——一上来就重训是最常见的浪费。①里⚠️「分母变了」每年坑人:CTR跌可能是曝光暴涨不是点击少,看比率永远同时看分子分母绝对值;顺便查模型版本号(10秒的事)。⭐⭐②拐点时间是信息量最大的数字——能把所有可能原因缩小到那个时间点附近的三五件事;形状直接指向类型:突变→发布/配置/上游;阶梯→数据源或口径变了;缓降→漂移不是故障;周期→定时任务。配一条变更时间线看板(发布+配置+上游产出+运营排期+外部事件),把③从半小时压到一分钟。⭐③的原则:止损优先于定位,能回滚先回滚(恢复了也顺便验证了原因)。④拆流量:只有一撮跌→范围缩小90%;全体均匀跌→多半是模型或全局配置。⑤⭐⭐SHAP对比异常时段vs正常时段,同时看「贡献变化」和「特征均值变化」:两个都变=输入变了(最常见);只有贡献变=作用变了,可能是模型或编码。拐点可以让机器找(两段方差和最小的切点),但⚠️它永远会返回一个拐点,所以必须看 z(跳变幅度 ÷ 段内波动),|z| 小于 3 就当作没有拐点。💀真实案例:Redis 扩容后 key 前缀配错一位 → 特征读超时 → 触发"设计好的降级"返回默认值;结果成功率 100%、错误率 0、P99 反而从 42ms 降到 26ms,只有拦截率从 1.8% 掉到 1.1% —— 一个只看服务健康的监控体系对它全盲发现延迟 31 小时,漏放约 4200 笔欺诈。补上「默认值占比」监控 + 降级链路打点后,同类问题发现延迟降到 4 分钟。⭐⭐记住这条速查技术指标全正常、业务指标却掉了 → 几乎总是降级/兜底/默认值(真故障会留下错误率,降级不会);其他高频对应:缓降=漂移不是故障、分数分布整体平移=某特征量纲变了、总体跌但每个人群都涨=流量结构变了(辛普森)。六法则:止损优先、⭐一次只改一个变量(慌了同时回滚+改配置+重启,恢复了但永远不知道原因)、相信数据不信记忆、边做边记、⭐先缩范围再看细节(时间→流量→特征三次二分 = 砍到 1/8)、⭐⭐「没报错」不等于「没出错」。七个坑里最贵的两个:一上来就重训(根因常在上游)和拐点按天看(把突变误判成漂移,然后去做了错误的重训决策)。

下一节 👉 12-AB实验你以为你懂了.md

打卡记录保存在你的浏览器里,首页能看到总进度