最近一次发布后接口变慢,别急着追某个 commit,先把发布窗口里的变化摊平
19:02 发布完成,19:09 `/trade/confirm` 的 P99 从 180ms 顶到 2.1s。后来把 19:00 到 19:15 的发布窗口摊开看,先跳出来的不是某个 commit,而是灰度开关和紧跟着启动的回填任务。
那天值班群里第一句是:“刚发完版,/trade/confirm 慢了,先看哪个 commit?”
我没急着开代码页,先把 19:00 到 19:15 这段发布时间窗里的东西拉到一张表里。表一出来,方向就变了。
| 时间 | 事件 | 现场信号 |
|---|---|---|
| 19:00 | 开始发布 trade-service v2026.03.24.3 | 老实例流量正常 |
| 19:02 | 新版本 6 个 Pod 全部 Ready | /trade/confirm P99 仍在 180ms 左右 |
| 19:04 | 灰度流量从 10% 放到 30% | 只有新 Pod 的该接口 RT 开始抬头,老 Pod 仍稳定 |
| 19:08 | 发布后任务 coupon-eligibility-backfill 自动启动 | 数据库连接池 pending 从 0 涨到 37 |
| 19:09 | /trade/confirm P99 180ms → 2.1s | 新 Pod 更明显,超时开始出现 |
| 19:12 | 暂停回填任务 | P99 快速回落到 320ms |
| 19:15 | 关闭新逻辑开关 coupon.enrich.enabled | 恢复到 200ms 内 |
这张表最有用的地方,不是“证明谁有锅”,而是把一个模糊的“发布后变慢”拆成了两段:
- 19:02 版本发完时,其实还没慢
- 19:08 之后,随着回填任务和新开关一起生效,慢点才真正站起来
如果一开始就只盯 git diff,这 6 分钟的差别很容易被抹掉。
先查什么,取决于慢点是跟着哪一分钟长出来的
这次现场里,真正有价值的第一问不是“改了哪段代码”,而是:
接口到底是在“发布完成”那一刻变慢,还是在“发布窗口里的另一个动作”发生后变慢?
这不是咬文嚼字。因为它直接决定你先翻哪一层证据。
上面这张时间表已经告诉我三件事:
- 不是所有变更都在 19:02 同时见效
- 新老实例分叉很明显,说明现场不只是代码本身
- 数据库等待是在发布后任务启动时一起冒出来的
到这一步,排查顺序已经不该是“先翻 commit → 再看别的”,而应该变成:
- 先对齐发布时间窗里的动作顺序
- 再看新实例和老实例的分叉
- 最后再决定要不要往代码细节里钻
这次真正把人带偏的,是“发布”两个字太大了
群里一开始说“发布后慢了”,大家脑子里默认都是同一个画面:
发版 → 某段新代码变慢 → 找出 commit
但真实现场不是这么单线条。这个发布包里一起动了四样东西:
- 新代码
- 新配置开关
- 新 Pod 批次
- 发布后自动触发的回填任务
其中最容易被漏掉的,反而是后两样,因为它们不在 git diff 里。
当时顺手拉了一眼配置中心的变更记录,能看到一个很关键的开关在新版本窗口里一起开了:
-coupon.enrich.enabled=false
+coupon.enrich.enabled=true
这个开关本身不一定单独把接口打慢,但它让 /trade/confirm 在结算时多了一次优惠资格补查;而 19:08 启动的回填任务也在扫同一批表。两个动作叠在一起,才把等待链拖长。
所以这次不是“代码没问题”,而是把“发布”误读成“只有代码在变”,才让第一轮判断走偏了。
新老实例分开看,价值往往比先翻代码还高
19:04 灰度流量放大后,面板很快出现了这种形状:
| 实例 | /trade/confirm P95 | 错误率 | 备注 |
|---|---|---|---|
| trade-6f8d9 old-1 | 210ms | 0.1% | 老版本 |
| trade-6f8d9 old-2 | 230ms | 0.1% | 老版本 |
| trade-a13c2 new-1 | 1.4s | 1.9% | 新版本 |
| trade-a13c2 new-2 | 1.7s | 2.4% | 新版本 |
只要看到这种新老分叉,脑子里就不能只剩“是不是代码回归”这一条路了。
因为新 Pod 比老 Pod 多出来的,不只是二进制包,还可能有:
- 配置来源不同
- 环境变量不同
- 节点资源不同
- 路由样本不同
- 启动后自动任务不同
这次现场里,继续往下拉日志就能接上:
19:08:11 INFO coupon-eligibility-backfill start batchSize=5000
19:08:34 WARN hikari-pool pending=37 active=64 max=64
19:09:02 WARN /trade/confirm cost=2148ms traceId=9f7c... pod=trade-a13c2-new-2
时间线、实例分叉、连接池日志,三样一拼,比单看代码 diff 更快把范围缩住。
这种发布窗口里,最该优先拉平的是哪几类变化
这次过后,我对“发布后接口慢先查什么”这件事,判断更明确了:
第一类:时间线
先把下面这些时间点放在同一条线上:
- 发布开始和完成
- 灰度开始和放量
- 配置开关生效
- 后台任务启动
- 指标抬头
- 人工动作(暂停任务、关开关、回滚)
如果没有这条线,后面很多讨论都只是印象流。
第二类:实例分叉
别急着用“全服务变慢”这种大词。先问一句:
- 是所有 Pod 一起坏,还是新 Pod 先坏?
新老实例一旦分叉,说明现场可以继续往配置、环境、路由和任务上切,不用一头扎进代码细节里盲找。
第三类:发布包之外的伴随动作
很多慢点根本不是版本一 Ready 就出现,而是跟着这些动作站起来:
- 预热
- 回填
- 索引重建
- 规则同步
- 灰度放量
这次最说明问题的,不是改动有多大,而是暂停回填后 RT 先回落。这比“猜测某个 commit 可疑”更接近现场事实。
回到标题里的那个问题:发布后接口慢,先查什么
如果只能先做一个动作,我会选:
先把发布窗口里的分钟级时间线、实例分叉和伴随动作摊到一张图上。
原因很简单。发布不是一个点,而是一串动作。接口慢也不是一句话,而是某一批实例、某一分钟、某一层等待先开始变差。
把这三件事先对齐,后面你再去看 commit、配置 diff、线程池、SQL,证据才会往同一个方向收。
这次现场最后当然也翻了代码,但真正把方向扳正的,不是代码页,而是那张 19:00 到 19:15 的发布窗口表。