系统是被打出来的 · 系统设计实战手册
PART 4 · 高可用与多活 · 第 16 章

混沌工程与容量规划
主动把系统弄坏

六年前,生产故障总在你没准备时敲门。今天你主持第一次生产游戏日:先写稳态,再圈爆炸半径,然后亲手注入那些熟悉的故障,看看预案究竟是护栏还是文档。

16 · 1 值班现场10:00,这一次,P0 是你亲手按下的

创业第 6 年,「秒抢」累计用户约 1.2 亿。公司 300 人,研发 80 人。你站在生产游戏日指挥台前,SRE、订单、支付、库存与客服负责人全部在线。大盘全绿,用户并不知道今天会有一台机器被故意拿走。

GAME DAY · ARMED · 计划注入于 10:05:00
[STEADY] 核心下单成功率 99.93%,P99 268ms
[GUARD]  自动回滚:成功率 < 99.5% 或 P99 > 500ms,连续 2 个窗口
[SCOPE]  单个订单单元、1% 生产流量、禁止触碰退款与对账任务
──────────────────────────────────────
实验计划:
注入缓存节点不可用 → 验证限流、回源保护与自动回滚

你没有从“杀哪台机器”开始,而是先让每个人复述停止条件。库存对账差异必须为 0;核心成功率不能低于 99.5%;P99 不能连续两个窗口超过 500ms;爆炸半径固定在一个单元的 1% 流量。任何一条越线,自动回滚,不等人投票。

预估峰值
120 万 QPS
● 下一次大促
压测验证容量
180 万 QPS
● 故障前稳态
容量水位
0.67
● 低于起步值 0.7
实验爆炸半径
1% 流量
● 单一订单单元

阿凯的手停在注入按钮上:「先从预发再演一遍,还是按计划上生产?」这不是胆量测试。你要判断的是,哪种最小动作能验证“生产流量、真实依赖和自动回滚共同工作”,同时不把学习成本转嫁给整站用户。

A的问题是跨过了太多未知。 机房级切换同时触碰流量调度、数据一致性、容量与网络,第一次生产实验很难判断失败来自哪一层。爆炸半径也远大于本次要验证的缓存回源护栏。

B方向反了——它放大的正是你想压住的东西。 全集群同时变慢会把回源洪峰直接交给数据库;“人工观察后再回滚”把停止时间交给会议延迟。实验若没有机器可执行的回滚条件,就在用用户等待换团队反应时间。

C方向没错,但证据不完整。 预发适合验证注入工具、指标和回滚脚本,却缺少生产流量形状、真实依赖配额与实际网络。它是生产实验的前置门槛,不能替代受控生产证据。

D该先做的就是它:用最小爆炸半径验证完整闭环。 一台缓存节点足以触发回源、限流、告警与回滚路径;单元边界和 1% 流量限制影响范围,机器执行停止条件又避免人工迟疑。实验有明确学习目标,也有明确刹车线。

16 · 2 原理容量给实验留余量,实验给预案还真相

容量规划先回答“峰值离验证上限还有多远”。不能把机器规格或理论吞吐当容量,分母必须是相同业务路径、相同数据形状下,由压测真正完成并持续稳定的吞吐。

容量水位 = 预估峰值流量 ÷ 已压测验证容量  (起步值 0.7)

代入下一次大促:预估峰值 120 万 QPS,已压测验证容量 180 万 QPS,水位是 120 ÷ 180 ≈ 0.67,低于 0.7 起步值。余下约 33% 不是闲置,它要吸收预测误差、长尾、单元故障与实验扰动。

0.7 不是行业红线,只是「秒抢」按本次条件推出的起步值——同一个数字放到别的系统身上未必安全,也未必需要留这么多余量。它至少取决于七件事:流量预估误差越大,水位目标要压得越低;冗余模型是 N+1 还是 N+2,决定同样峰值下还能不能把日常水位设得更高;故障域大小越大,一次故障吃掉的验证容量比例越高,水位目标要相应下调;扩容时延越短,越敢把水位推向上限;服务时间方差越重尾,越容易在峰值附近排队;降级能力越强,能优雅丢非核心流量的系统水位上限越高;恢复期追平负载越大,故障恢复后短时叠加到正常峰值上的积压请求越多。这七项没有一项是「秒抢」独有的行业常数,每次大促前都要重新核算。

峰值 120 万
0.67
120 ÷ 180
峰值多 15%
0.77
138 ÷ 180
失去 1/4 容量
0.89
120 ÷ 135
起步值
0.70
超过即补容量或减流

两个压力测试说明为什么 0.67 仍需谨慎:峰值预测若高 15%,水位变成 138/180≈0.77;若故障让验证容量损失四分之一,只剩 135 万 QPS,水位变成 120/135≈0.89。容量规划要同时给正常峰值与故障后容量算账。

混沌工程不是随机破坏,而是四步可证伪的实验:

步骤 本次游戏日 缺了会怎样
1 · 稳态假设 下单成功率 ≥99.5%,P99 ≤500ms,库存对账差异为 0 实验结束后不知道系统算“扛住”还是“没报警”
2 · 最小爆炸半径 一个订单单元、1% 流量、不触碰退款与对账 一次学习动作可能升级成整站事故
3 · 注入 下线单台缓存节点,观察回源、限流与依赖传播 没有受控变量,无法归因预案是否生效
4 · 自动回滚 成功率或 P99 连续越线即恢复节点并切走实验流量 停止实验依赖人工判断,爆炸半径随会议时间扩大
稳态假设 → 最小爆炸半径 → 注入 → 自动回滚

「没演练过的预案不是预案,是文档。」限流开关写在手册里,不等于它能在缓存节点消失时及时触发;多活拓扑画在架构图里,也不等于容量和数据边界真的能接管。实验的价值是让预案面对真实因果链。

容量余量是刹车距离,混沌实验是刹车测试。没有余量,团队不敢实验;没有实验,团队不知道余量在故障时还剩多少。

16 · 3 病历卡十四张病历,三类系统性体质

本章不新增病历卡。收官总检要回答的不是“每张卡叫什么”,而是“游戏日先打哪一类”。No.01–14 各放入一个主类别:放大类关注“一个小触发怎样制造更多负载”,一致性类关注“多份真相怎样分叉”,单点类关注“共享瓶颈怎样让集群余量失效”。分类不是说卡片只有一种属性,而是为了决定先做哪种实验、稳态盯哪条曲线、事后怎样证明没留后遗症。

体质 · 卡片 共同机理 先打的实验 · 稳态看什么 · 怎样证明没留后遗症
放大类
No.04 05 06 07 08 09 11
小触发在系统内部长成比自身大得多的负载:并发回源、逐层重试、同步唤醒、沿依赖传播、按时间累积欠债。 注入一个小而可回收的触发——一次热点过期、一次下游超时、拖慢一个依赖、停一个消费者。稳态看内部负载 ÷ 入口负载有没有被放大,触发撤掉后曲线是否自己回落;回不落的就是死亡螺旋,靠限流与慢放量收场。后遗症看曲线是否回到稳态。
一致性类
No.02 03 12 13 14
同一份事实在时间或故障中被复制成两个版本:主与从、缓存与 DB、生产与消费、两个持有者、两个主。 故意让第二步失败或延迟——丢一次缓存失效、拉长复制延迟、重投一条消息、让临界区超过租期、隔离两地链路。稳态看对账差异笔数与并发写者数,性能大盘可能全绿,证据只在业务账上。后遗症看账能否对平,不看进程是否重启成功。
单点类
No.01 10
一处共享闸门或一个被钉住的子集先耗尽,其余节点的余量无法接力。 把一处打热或拖慢——注入慢查询、注入热点 key。稳态看最热 ÷ 平均与总容量余量的背离:集群平均健康、局部已经打满,就是单点。后遗症看把那一处摘掉或打散后,其余容量能否接住。

三类体质,三种爆炸半径。放大类的实验最容易越界,因为放大本身就是它的机理,先在预发验证“回落”是否成立;一致性类的实验对业务账动手,必须先备好对账脚本与补偿路径;单点类的实验半径最小,也最适合作为第一次生产游戏日的开场。

附录 A机制轴索引 逐张的机理、症状形状与处方不在这里重抄。附录图鉴给 17 张卡(含附录新增的 No.15–17)都标了这套体质标签,作为形状之外的第二轴,并配了同形状候选的鉴别诊断表。附录图鉴 · 机制轴索引 →

总检完成后,每张卡都应对应一个可执行实验:输入是什么、稳态看什么、爆炸半径多大、何时回滚、如何证明数据没有留下后遗症。附录图鉴是索引,游戏日计划才是处方。

16 · 5 权衡生产是真相,预发是护栏

今天这场游戏日注入的是可用性类故障——缓存节点掉线、依赖超时,坏了就降级,最坏结果是用户等得久一点。会后 Mia 问了下一个问题:数据一致性类的故障要不要也搬到生产上?跨机房复制中断、库存与订单对账偏离——这类实验一旦停止条件失效,赔上的不是可用性,是数据,而数据错了没有回滚按钮。

三个方案都对,差别在于:你需要多强的证据、组织能承担多大爆炸半径,以及这一类实验错了之后还能不能收回来。最后这个维度,正是可用性类实验和数据类实验的分水岭。

决策点 D2

下一类实验是数据一致性故障注入。它应该停在预发,还是也进入生产?

A · 只在预发:爆炸半径容易控制,可频繁验证注入工具、告警和回滚脚本,不影响真实用户。 账单:预发流量、数据量、依赖配额和网络拓扑常与生产不同,得到的是工具证据,不是生产稳态证据。适合团队刚起步、停止条件尚未自动化的阶段——也适合数据类实验的起步阶段:错了能重建环境,而不是去补生产账。

B · 直接生产:真实流量、真实依赖和真实容量共同参与,最容易暴露只在生产存在的耦合。 账单:未知项多,错误停止条件会把实验变事故。而且对数据类实验要说清楚一件事:自动化能做的是「停止注入 + 对旧写入 fencing + 切走流量」,它撤销不了已经写错的数据——所谓"自动回滚"在可用性类实验里成立,在数据类实验里只是止血。因此前置条件更硬:成熟单元化、自动停止与写入 fencing、事前就绪的审计 / 对账 / 修复流程、明确的 RPO,以及跨团队指挥。适合已多次演练同类故障、爆炸半径可硬隔离的组织——当某个耦合只在生产存在、预发怎么造都造不出来时,它是唯一能给出证据的选项。

C · 分级晋升:每一级都复用同一稳态假设、自动停止与写入 fencing 条件,以及对账与修复门禁;只有前一级证据通过才扩大真实度与范围。 账单:它不是免费的折中。数据类实验要走完这条晋升链,前提是先建出影子链路和对账门禁——这两样「秒抢」现在都没有,而它们本身就是季度级工程。在它们建成之前选 C,你实际拿到的只有预发那一级的证据,却付了 C 的治理成本。团队还可能在预发通过后迟迟不敢迈进生产,把分级变成拖延的正当理由。适合已具备影子链路与对账门禁、要把混沌工程做成长期能力的阶段。

「秒抢」的选择:按实验类别分开定。可用性类故障(就是今天注入的那类)继续走 C——它已经走到 1% 生产流量这一级,证据链完整,继续扩大范围即可。数据一致性类故障先按 A 停在预发,同时把影子链路和对账门禁排进下个季度;等这两样建成,再让它进入 C 的分级通道。同一个组织在同一时间对两类实验采用不同成熟度,这不是不彻底,而是承认「错了能不能收回来」这件事,本来就该改变你的风险偏好。只有当某个数据类耦合被确认只在生产存在时,才付 B 的代价,并把爆炸半径硬隔离到单个单元。

条件翻转

把上面判词里那句「先按 A 停在预发」的前提改掉:假设影子链路与对账门禁在这个季度已经建成并验收通过——生产写入能实时复制到独立环境做全量重放,对账差异超过阈值会自动挂起本轮实验,不需要人工去对账。数据一致性类实验,现在还应该继续停在预发吗?

翻转变量是「影子链路与对账门禁是否已经建成」。 原判词选 A 的唯一理由,就是这两样「现在都没有」——不是数据类实验本身危险,而是没有护栏时,C 的分级治理成本你已经在付,却只拿到 A 那一级的证据。这个事实一旦翻转,继续选 A 就从「谨慎」变成「过度保守」:影子链路能做全量重放,对账门禁能自动挂起,你已经有工具把「错了能不能收回来」这件事从「不能」变成「能,而且是机器判断的」。

正确答案变成 C,但要从最低一级重新起跑,不能直接搬到可用性类实验已经验证过的 1% 生产这一级——两类实验的历史证据不能互相借用,数据类实验的第一次生产暴露,仍然要重新走一遍稳态假设、最小爆炸半径与自动回滚。B 依然不成立:护栏建成不等于护栏已被验证,分级晋升的意义正是让新建的机制先在低成本环境里证明自己,跳过阶梯直接生产,等于让刚建好的对账门禁第一次上场就赌上真实用户数据。

16 · 6 复盘从一次可控失败,回望六年架构

回放第一次生产游戏日——顺序依旧是知识:先确认稳态,再注入,越线自动退,最后用数据决定是否扩大。

10:00

锁定稳态。记录成功率 99.93%、P99 268ms、库存对账差异 0;确认实验单元只承接 1% 流量,回滚脚本已预执行。

10:05

注入节点不可用。单台缓存节点下线,命中率在实验单元短降,互斥回源与网关配额生效;核心成功率最低 99.61%,P99 峰值 438ms。

10:08

验证自动回滚。人为将实验阈值临时收紧到 P99 400ms,系统自动恢复节点并切走实验流量;42 秒内 P99 回到 280ms,对账仍为 0。

10:20

记录证据,不追求“扛住”。团队确认回源保护有效,也发现一个非核心推荐接口共享了实验缓存池;修复隔离前,不扩大到第二个单元。

壹
稳态必须写成机器能判断的条件。“大盘看起来正常”无法自动回滚;成功率、P99、对账差异和连续窗口才是可执行合同。
貳
实验成功不等于系统没有波动。真正的成功是波动留在预算内、停止条件有效、数据没有后遗症,并且团队获得可复用证据。
叁
容量规划要按故障后重算。正常态 0.67 不代表失去一个单元后仍安全;每种故障都要重新计算分母,而不是沿用健康集群容量。

第 1 章的第 0 帧,与今天的最终形态

第 1 章说,后面每一章都在改那张基线图。现在把它与六年后的系统并排放回桌面:左边不是幼稚,右边也不是荣耀;它们分别是当时流量、团队与故障证据下,愿意支付的架构账单。

「秒抢」v1.0 · 上线当天的全部架构
浏览器
手机 H5
→
Nginx
反向代理
静态文件
TLS 终止
→
应用(单体)
一个进程
路由 / 业务 / 定时任务
全都在里面
→
MySQL
一台
无从库
连接池 20
没有的东西:缓存 · 消息队列 · 从库 · 网关 · 熔断 · 限流 · 监控大盘 · 第二台机器
有的东西:一台 4 核 8G 云服务器,Nginx 和应用和 MySQL 挤在上面
「秒抢」v16 · 被六年流量打出的最终形态
浏览器 / App
退避 · 凭证
多端会话
→
CDN / WAF
静态化
风控前置
→
网关 / 漏斗
限流 · 排队
降级 · 凭证
→
服务单元
订单 · 库存 · 支付
熔断 · 舱壁 · 幂等
→
数据层
缓存 · MQ · 分片
主从 · 多活 · 对账
观测
metrics · traces · logs
症状与业务对账
容量
逐层预算
故障后水位重算
演练
稳态假设
注入 · 自动回滚
每一个新增方框都有病历:它不是因为架构图缺一块而出现,而是因为连接池、复制、缓存、重试、热点、消息、锁与机房曾经让系统付过账。
第 1 章那台没有缓存、消息队列、从库、网关、熔断、限流、监控大盘和第二台机器的单体,并没有“设计错”。它完成了当时的任务。右边这张图也不是终点,它只是把过去的疼痛变成了今天的护栏。系统是被打出来的,但被打之前,你可以先打它。

关卡一留给你的那个问题——你的系统里有没有「其实靠运气在兜底」的东西——答案就是这一章:把它找出来,在演练里故意拿掉,看系统倒不倒。运气不是被消灭的,是被演练成了护栏。

毕业飞行:轮到你自己指挥了

前面十六章,现场信息是别人给你摆好的:告警已经分好轻重,指标已经挑好该看哪几个,选项也已经写成 A/B/C/D 供你挑。这一次不是。下面这道题不给标准输入,只给一个新业务、一堆不完整甚至互相打架的证据,和两次真实会打断你计划的事件。它什么都不保存,刷新就没了——所以先把答案写在文本框里,写不出来也要写下卡在哪一步,那本身就是诊断。写完再点「展开基准答案」,对照自评清单给自己打分,而不是找一个「正确答案」。

交卷 E1 · 第一阶段

波普工坊直播秒杀 · 只给你这些,别的自己猜

「秒抢」中台孵化的潮玩品牌「波普工坊」今晚 8 点开一场限量手办直播发售,总量 5000 件,但不是一次性放出——主播每卖完一批,运营就临时申请追加,业务内部管这叫「realtime restock」。目前已经上了两批,直播间在线人数在 8 万到 35 万之间波动,每次主播口播「补货了」都会出现二次尖峰,具体倍数没人测过。你是这场直播抽调来的技术保障,PM 只甩给你下面这块屏幕,其余的要你自己去问、自己去假设、自己写下来。

直播保障群 · 19:47 · 第 3 批补货 5 分钟后上架
[STEADY] 库存服务 QPS 平稳,无告警,P99 43ms
[GAP]  支付成功回调量比下单成功量少一截——粗看约一成,缺口是超时还是渠道拒绝,监控没拆
[GAP]  客服:「有人说点了『抢到』,库存页显示已扣,支付转圈很久最后失败」——只有零星截图,没有量级
──────────────────────────────────────
已发生(第 2 批上架时):
日志里出现一批「库存扣减成功但订单创建失败」的记录,运营群贴了几张截图,具体条数没人统计
运营刚甩来一句:
「第 3 批比前两批数量都大,5 分钟后上,要不要先改点什么?」

注意:库存总量、每批具体数量、二次尖峰倍数、支付缺口的真实占比——这些数字都没给全,因为现场也没有全。要交这 4 样,每一样都要能被你自己检查:

① 一页容量契约(写清楚假设是什么、上下界是多少,不要求算准,要求算法可复查) ② 一张架构草图(文字描述这一批请求会经过哪些环节即可,不用画图) ③ 前三个风险,每个配一个病历卡编号和「如果我错了会看到什么信号」——如果 17 张卡里没有一张对得上(这道题里至少有一个风险是这样),写「无卡」并自己给它起名,说明它和最近的那张卡差在哪 ④ 接下来第 1 分钟做什么、前 10 分钟做什么,并区分哪些是止血、哪些是诊断。

写不出来也要写下卡在哪一步——那本身就是有用的诊断。答案不会被保存,刷新即失。

你还没写。这是这类练习专门检验你的独立输出,先写下来再看——再点一次仍会展开。

基准答案 · 容量契约(一份合格答案长什么样)

先承认数字缺口,再用双轨给上下界,而不是等运营把准数发过来:假设 35 万在线峰值里,愿意在补货 30 秒内下单的比例是 5%–15%(明确写「这是假设,不是已知量」),对应 1.75 万–5.25 万意图请求;单批库存按运营口径「越来越大」估成 200–1000 件区间,分别推一遍资源预算。契约还要写一条失效条件:如果第 3 批实际数量或响应时间偏离这个区间超过 2 倍,契约作废,需要重新签。没有准数时,「区间 + 失效条件」比「编一个看起来精确的假数字」更诚实,也更可用。

基准答案 · 架构草图

入口(直播间弹窗按钮)→ 网关限速与候选凭证 → 库存服务原子扣减 → 订单创建 → 异步通知支付 → 支付回调更新订单状态;库存扣减与订单创建之间需要点名它们是不是同一个事务——这正是「已发生」那条日志在暗示的疑点。合格答案会明确画出「扣减成功、创建失败」这条路径现在有没有补偿,而不是默认它一定有。

基准答案 · 前三风险 + 病历卡编号

① 无卡 · 突发热点导致的二次尖峰(不是 No.08 惊群——第 7 章的惊群指大量等待者因统一超时、固定 backoff 或统一 TTL 而同步重发;这里是主播口播后真实用户同时涌入,属于合法需求尖峰):信号是二次尖峰的 QPS 曲线短时垂直跳变。② No.01 连接池耗尽:库存服务当前 QPS 看着平稳不代表没风险——平稳可能是连接池在排队吸收峰值,信号是获取连接的等待时间是否悄悄变长。③ No.11 消息积压,或「跨服务部分提交」——17 张卡里没有这张(最近的是 No.03,但 No.03 讲的是缓存 vs DB,不是库存服务 vs 订单服务),此刻证据不够二选一:「扣减成功、订单创建失败」既可能是异步通知延迟(积压),也可能是两步操作本来就不在一个事务里(双写不一致);合格答案会诚实写出「我现在分不清,需要哪一条监控才能分清」,而不是硬选一个。

基准答案 · 第 1 分钟 / 前 10 分钟动作

第 1 分钟(止血,不诊断):把「支付回调缺口」和「扣减成功订单创建失败」这两条从定性报告升级成计数器,哪怕只是临时加一行日志计数;不改任何限流或扩容参数——现在还不知道该往哪个方向调。前 10 分钟(诊断,为第 3 批做准备):用现有截图和日志估算「已发生」那批异常的量级下限;给第 3 批设一个明确的观察指标(支付回调缺口是否随本批扩大),而不是先动手扩容或限流。「先弄清楚问题往哪个方向移动,再决定往哪个方向治」本身就是及格线。

自评:你的答案里有没有这几样

  • 有没有至少两处明确写「这是我的假设」,而不是把猜测当成已知事实
  • 容量契约有没有给出上下界,而不是一个看起来精确的单一数字
  • 前三风险是不是每条都配了「如果我错了,会看到什么信号」,而不是只给了病历卡编号
  • 第 1 分钟和前 10 分钟有没有分开写,第 1 分钟有没有忍住不去动限流或扩容参数
  • 有没有至少一处诚实写「现在证据不够,判断不了」——这比编一个自信的错误结论更接近及格
交卷 E2 · 第二阶段

5 分钟后:第 3 批上架,后果取决于你刚才怎么选

下面四段现场,只有一段和你有关。怎么定:把你在 E1 写的「第 1 分钟 / 前 10 分钟」里 最先真正改变系统行为的那一个动作(不是最先写下的那一句)归到下面四类之一—— 加容量走 X,收紧入口走 Y,改到达过程走 Z,只加观测不动参数走 W。 若你写了不止一项,按 X > Y > Z > W 取影响最大的那一项。 W 是 E1 基准答案推荐的做法,如果你照它做了,你就该走 W——这不是保守,是这道题认的答案。

四段都读完再挑。这正是这道题想练的事:同一个初始状态,选择不同,后面接到的证据也不同—— 而且不同分支拿到的证据强度并不相等,这本身就是要学的东西。

分支 X · 如果你刚才更倾向「给库存服务 / 网关加机器」
[STEADY] 库存服务撑住了,峰值 QPS 到平时 4 倍也没告警
[WORSE]  支付回调缺口从约 10% 扩大到约 23%
客服新增一批带图截图:
「库存页显示已抢到,15 秒后订单自动取消,库存又跳回了 +1」
分支 Y · 如果你刚才更倾向「收紧网关候选配额」
[STEADY] 支付回调缺口没有扩大,维持在约 10%
[NEW]  弹幕开始截图「显示无库存」,随后在别的平台看到同款仍在售
运营新提问:
「是不是把量控得太死,把真实需求挡在外面了?」
同时,被限住的那批请求里有 37 条日志显示:
「扣减成功、订单创建失败」——即使限了流,这条缝依然在漏
分支 Z · 如果你刚才更倾向「拆批 / 延迟 / 错峰——不加机器也不拒人」
[STEADY] 二次尖峰被摊平,库存服务 QPS 峰值只到平时 1.8 倍
[WORSE]  支付回调缺口没有随之改善,仍在约 10%
客服同样收到那批截图:
「库存页显示已抢到,15 秒后订单自动取消,库存又跳回了 +1」
——削峰治住了「涌入」,没治住「缝」。
分支 W · 如果你刚才只加了观测、没动任何参数
[NEW]  你临时加的计数器出数了:本批「扣减成功、订单创建失败」214 次
[STEADY] 支付回调缺口维持约 10%,且不随本批 QPS 变化
客服同样收到那批截图:
「库存页显示已抢到,15 秒后订单自动取消,库存又跳回了 +1」
——你付出的代价是这一批没有被保护;换来的是四个分支里最干净的证据。

要交 3 样:① 更新后的前三风险排序——哪个从候选里掉出去了,哪个新冒出来了,为什么 ② 你现在最想补的一条监控——把哪个「证据缺口」变成一个可以看的数字 ③ 接下来 5 分钟的动作——注意这一步是止血,不是重新设计整套系统。

两个分支的正确诊断方向其实相通——这也是自评的一部分:你有没有看出这一点。答案不会被保存。

你还没写。先写下你选的分支和判断,再看基准答案——再点一次仍会展开。

基准答案 · 四个分支指向同一处,但证据强度不同

分支 X(扩容)暴露的是:把压力从库存层挤过去之后,「扣减成功、订单未真正完成」这条缝会被更大的流量放大——这更像是库存扣减和订单创建不是一个原子操作(呼应 No.03 的双写分叉思路,但此刻是「库存 vs 订单」而不是「缓存 vs DB」,不能照抄第 4 章的处方,要重新验证)。分支 Y(限流)没有放大这条缝,但把「证据不足」换成了「业务不满」——你现在要用数据回答限流阈值是不是过度保守,而不是靠感觉。分支 Z(改到达过程)把二次尖峰摊平了,这是第 9 章削峰填谷的正确应用——但它同时证明了一件更重要的事:缺口不随 QPS 变化,说明这条缝不是过载造成的,削峰治不了它。分支 W(只加观测)付出的代价是这一批没被保护,换来的却是四条里最干净的证据:一个确切的计数(214 次)和一个关键观察(缺口不随本批 QPS 变化)。这正是 E1 基准答案让你忍住不动参数的理由——在不知道往哪个方向治之前,任何调参都会把「过载成分」和「固定成分」搅在一起,让你更难分辨。四个分支共同的正确下一步,都是先把「扣减成功但订单/支付未完成」这条链路的计数器和自动补偿补上,因为不管选哪条路,这条缝都没有被治好,只是被不同程度地照亮了。

基准答案 · 要补的监控

「库存扣减成功时间戳」与「订单最终状态确认时间戳」之间的差值分布,按批次拆开看 P50/P99;这一条能同时回答分支 X 的「缝有没有变宽」、分支 Z 的「削峰有没有治到点子上」和分支 W 的「缺口到底是不是过载引起的」,是这一步性价比最高的一个指标。唯独分支 Y 的「限流有没有误伤」它答不了——这条指标只统计已经扣减成功的请求,而被 429 挡在门外的人从没走到扣减这一步,根本不在这个分布里。走 Y 的读者要多补一条:网关拒绝数(429 / 候选未中)与本批 Redis 剩余库存同屏——余量 > 0 且拒绝占比高,才叫误伤;余量已经是 0,运营那句「控太死」就不成立。

自评:你的答案里有没有这几样

  • 有没有说出「哪个风险从前三名掉出去了」,而不是只在旧清单上加新条目
  • 补的监控有没有说清它对哪几个分支有效、对哪个分支是盲的——一条只数「已扣减者」的指标,看不见被拒在门外的人
  • 接下来 5 分钟的动作是不是止血(比如给已知缝隙加补偿或告警),而没有滑回「重新设计整套系统」
  • 有没有看出四个分支背后其实是同一条缝——如果没看出来,把四段现场的最后一句并排读一遍,比对着找
  • 有没有注意到不同分支拿到的证据强度不一样:X 把缝放大了、Z 证明了它与过载无关、W 给出了确切计数。真实排障里,你选的第一个动作会决定你接下来能看见什么——这是这道题最想让你带走的东西

16 · 7 自测你敢按下按钮,也知道何时松手吗

Q1 · 看形状诊断

网络隔离后,两边写入曲线呈 ██‖██,两个单元都认为自己可写。最先要验证哪类护栏?

B。双线分叉是 No.14 脑裂,核心是限制写权限而不是提高吞吐。D 很诱人,因为用户会看到失败,但重试只会把冲突写得更多;先用多数派与 fencing 收回过期主的权限。

Q2 · 容量算术

预估峰值 84 万 QPS,已压测验证容量 120 万 QPS。容量水位是多少,处于什么位置?

B。84 ÷ 120 = 0.70,已经到起步值,是否需要继续观察还要看误差、冗余与故障域这些具体条件。C 是最诱人的错误项,它把分子分母颠倒成 120/84;容量水位比较的是需求占验证容量的比例。

Q3 · 实验顺序

下面哪一组是混沌实验的正确顺序?

B。先定义可证伪的稳态,再硬限制影响范围,注入后由机器执行停止条件。A 很诱人,因为许多临时演练确实这样做,但注入后才定义成功标准,会让团队按结果修改题目。

Q4 · 故障后容量

健康态验证容量 180 万 QPS,故障后只剩 135 万;预估峰值仍是 120 万。为什么不能继续沿用健康态 0.67?

B。120/135≈0.89,健康集群的验证容量已经不是当前分母。A 是诱人的乐观假设,但故障不会替你减少用户需求;必须补容量或从入口减流。

🎯 面试映射 · 本章直接覆盖的高频追问

「如何保障大促稳定性?」 答题骨架:峰值估算与故障后容量 → 分层漏斗与核心优先级 → 限流/熔断/舱壁 → 数据一致性与对账 → 预案演练 → 值班、回滚与复盘。
「混沌实验怎么起步?」 答题骨架:选已知故障与单一稳态指标 → 预发验证工具 → 最小生产单元 → 自动停止条件 → 一次只改一个变量 → 证据通过再扩大。
「容量规划怎么做?」 答题骨架:业务峰值与形状 → 压测验证容量 → 水位起步值 0.7(按误差、冗余、故障域、恢复能力上下调) → 按故障场景重算分母 → 补容量或减流 → 用生产指标持续校准。反直觉加分点:余量是可验证的故障预算。

接下来 4 周:把「秒抢」的方法搬回你自己的系统

合上书不等于练完。下面这条路径没有唯一顺序,但有一个默认节奏:先体检,再写预案,再拿真实故障验证预案,最后用计时面试逼自己在压力下把这套方法完整说一遍。每周都给了交付物、完成标准和建议时长——时长是给忙碌工程师的参考值,不是及格线。

第 1 周
给自己的系统做体检
交付物:请求链路图 + 一张容量表 + 用病历卡列出前三个风险与证据缺口。完成标准:每个风险都能说出「如果这个假设错了,我会在哪张监控上先看到」。建议时长:4–6 小时。
第 2 周
写三份故障 Runbook
交付物:从第 1 周的前三风险里选三个,各写一份可执行 Runbook。完成标准:不看着 Runbook 也能把第一分钟动作背出来,且第一分钟动作里没有「先开会讨论」这种非机器可判断的步骤。建议时长:3–5 小时,一个风险一份,不必一次写完。
第 3 周
做一次预发游戏日
交付物:稳态假设、爆炸半径、停止条件、恢复证明,四样都写成机器能判断的条件,再实际注入一次第 2 周的某个故障。完成标准:停止条件被触发时,是系统自动停的,不是靠人盯着仪表盘喊停。建议时长:半天,含事后复盘。
第 4 周
三次计时面试自测
交付物:秒杀类、非交易高读类、异步协作类各一次,按附录 B 标时计时,用同一张评分表复盘三次表现。完成标准:能在被打断、被追问之后仍守住核心不变量,而不是从头重讲一遍。建议时长:每次 35 分钟 + 15 分钟复盘(按附录 B 的标时:每题两阶段 20 + 15 分钟)。

四份模板可以直接复制去填:

第 1 周 · 系统体检
================================
① 请求链路(入口到最终写入,标出每一跳的协议与超时):

② 容量表(每一层:实测 QPS / 理论上限 / 当前水位 / 瓶颈猜测):

③ 前三风险(病历卡编号 + 触发条件 + 证据缺口——我现在还不知道什么):
 1.
 2.
 3.
第 2 周 · 故障 Runbook(风险 #__)
================================
症状(怎么发现):
稳态判据(正常时这几个数字应该是多少):
第一分钟动作(止血,机器可判断,不含"开会讨论"):
升级条件(什么时候喊人 / 切预案):
恢复步骤:
恢复证明(怎么证明系统和数据都没留后遗症,不是看着正常):
第 3 周 · 预发游戏日实验卡
================================
稳态假设(成功率 / 延迟 / 对账差异的具体数字):
最小爆炸半径(哪个环境 / 哪一部分流量 / 排除哪些操作):
注入内容(具体故障,一次只改一个变量):
停止条件(机器可判断的越线阈值):
恢复证明(实验结束后如何证明系统和数据都没有后遗症):
第 4 周 · 计时面试复盘表
================================
题型:□秒杀类 □非交易高读类 □异步协作类 时长:45 分钟
需求澄清用时:___ 分钟(参考 ≤5)
估算是否量化,且写出了假设:□是 □否
架构是否覆盖失败路径,不只是 happy path:□是 □否
是否主动说出权衡,而不是等面试官问:□是 □否
被追问后能否守住核心不变量:□是 □否
下一次要改进的一件事:

第 4 周的追问树、评分尺和计时 mock 题,见 附录 B · 面试映射总表——48 个高频追问是速查表,用来对照你在计时面试里漏答了哪一类。