985 字
约 3 分钟
12
S27-J01 · 分布式可靠性四件套:业务 Key、Single-flight、僵尸 Owner、Stage
适用读者:要解决"重复请求、缓存击穿、任务卡死"的人。
前置:S17-J03(幂等)、S12-J01(击穿)。
实测环境:Python 并发模拟(2026-11-06)。
覆盖旧文:131(796) 128(411) 125(391) 129(413) 130(455) + 相关 = 2,596 点。
覆盖扩展:E-S27-006(业务 Key)、E-S27-007(Single-flight)。
上一篇:S27-B04 · 微信生态:扫码登录与支付
下一篇:S27-J02 · 缓存预热与分布式锁。
1. 一句话结论
四个问题其实是同一件事的四种表现:谁在"重复做同一件事",以及"谁占着资源不干活"。
| 问题 | 本质 | 解法 |
|---|---|---|
| 业务 Key | 同一业务被重复提交 | 唯一键 + 幂等 |
| Single-flight | 同一数据被并发请求 | 合并为一次上游调用 |
| 僵尸 Owner | 锁/任务持有者已死 | 租约 + 心跳 + 过期 |
| Stage 标签 | 请求生命周期不可见 | 统一阶段标记 |
2. 业务 Key:唯一性的落点(E-S27-006)
| 落点 | 优点 | 缺点 |
|---|---|---|
| 客户端生成 key | 提前防重 | 可伪造 |
| 服务端按业务要素生成 | 可信 | 要素要选对 |
| 数据库唯一索引 | 最终兜底 | 冲突要处理 |
判据:"这个 key 能唯一标识"一次业务意图"吗?" 不能(如时间戳)→ 会误判。
3. Single-flight:实测 41 次 → 1 次(E-S27-007)
模拟:100 个并发请求同一 key,上游耗时 20ms。
| 方案 | 上游调用 | 耗时 |
|---|---|---|
| 无合并 | 41 次 | 54 ms |
| Single-flight | 1 次 | 72 ms |
读法:上游调用降 41 倍,代价是少量延迟(等第一次完成)。
| 实现要点 | 说明 |
|---|---|
| 同 key 只放一个请求进去 | 其余等待结果 |
| 失败要唤醒等待者 | 否则全部挂住 |
| 超时要设上限 | 防雪崩(S12-J01) |
4. 僵尸 Owner 与租约
| 问题 | 后果 |
|---|---|
| 持有者崩溃未释放 | 资源永久锁死 |
| 网络分区 | 两个"Owner"并存 |
解法三件套:
① 租约(lease):锁有 TTL
② 心跳续约:活着就续,死了就过期
③ 单调递增的 fencing token:旧 Owner 的写被拒(防脑裂)
"为什么心跳不够":GC 停顿/网络抖动会导致"以为还活着"——fencing token 才是最终防线(S12-J03 的锁误删同源)。
5. Stage 标签:让请求可观测
| 阶段 | 用途 |
|---|---|
| received | 入口 |
| validated | 参数通过 |
| upstream_called | 调了外部 |
| persisted | 落库 |
| done/failed | 结束 |
"为什么要统一标签":排障时能一眼看出"卡在哪个阶段"——比翻日志快得多(S17-A02)。
6. 五个误区
| 误区 | 真相 |
|---|---|
| "唯一索引就够了" | 冲突异常要转成业务语义 |
| "合并请求会降低吞吐" | 实测上游降 41 倍 |
| "锁有过期时间就安全" | 需 fencing token |
| "心跳能证明活着" | GC/网络会骗人 |
| "阶段日志可有可无" | 排障效率差一倍 |
7. 自检
- 四个问题的共同本质?
- 业务 Key 该在哪生成?
- 实测里 Single-flight 把上游调用从多少降到多少?
- 防僵尸 Owner 的三件套?
- 统一 Stage 标签的价值?
8. 下一篇
S27-J02 · 缓存预热与分布式锁
可靠性讲完,下一篇讲"预热为什么总和锁一起出现"。
本篇属于系列《S27 · 杂项归档与工具实测》。旧文来源:A-131/128/125/129/130 + 相关卡;扩展点 E-S27-006/007。Single-flight 数据为 Python 并发模拟。
S27-J01 · 分布式可靠性四件套:业务 Key、Single-flight、僵尸 Owner、Stage
https://www.clxhxhhr.top/posts/5619/ 评论
0 条
还没有评论,先写一条吧。