六年前,生产故障总在你没准备时敲门。今天你主持第一次生产游戏日:先写稳态,再圈爆炸半径,然后亲手注入那些熟悉的故障,看看预案究竟是护栏还是文档。
创业第 6 年,「秒抢」累计用户约 1.2 亿。公司 300 人,研发 80 人。你站在生产游戏日指挥台前,SRE、订单、支付、库存与客服负责人全部在线。大盘全绿,用户并不知道今天会有一台机器被故意拿走。
你没有从“杀哪台机器”开始,而是先让每个人复述停止条件。库存对账差异必须为 0;核心成功率不能低于 99.5%;P99 不能连续两个窗口超过 500ms;爆炸半径固定在一个单元的 1% 流量。任何一条越线,自动回滚,不等人投票。
阿凯的手停在注入按钮上:「先从预发再演一遍,还是按计划上生产?」这不是胆量测试。你要判断的是,哪种最小动作能验证“生产流量、真实依赖和自动回滚共同工作”,同时不把学习成本转嫁给整站用户。
A的问题是跨过了太多未知。 机房级切换同时触碰流量调度、数据一致性、容量与网络,第一次生产实验很难判断失败来自哪一层。爆炸半径也远大于本次要验证的缓存回源护栏。
B方向反了——它放大的正是你想压住的东西。 全集群同时变慢会把回源洪峰直接交给数据库;“人工观察后再回滚”把停止时间交给会议延迟。实验若没有机器可执行的回滚条件,就在用用户等待换团队反应时间。
C方向没错,但证据不完整。 预发适合验证注入工具、指标和回滚脚本,却缺少生产流量形状、真实依赖配额与实际网络。它是生产实验的前置门槛,不能替代受控生产证据。
D该先做的就是它:用最小爆炸半径验证完整闭环。 一台缓存节点足以触发回源、限流、告警与回滚路径;单元边界和 1% 流量限制影响范围,机器执行停止条件又避免人工迟疑。实验有明确学习目标,也有明确刹车线。
容量规划先回答“峰值离验证上限还有多远”。不能把机器规格或理论吞吐当容量,分母必须是相同业务路径、相同数据形状下,由压测真正完成并持续稳定的吞吐。
代入下一次大促:预估峰值 120 万 QPS,已压测验证容量 180 万 QPS,水位是 120 ÷ 180 ≈ 0.67,低于 0.7 起步值。余下约 33% 不是闲置,它要吸收预测误差、长尾、单元故障与实验扰动。
0.7 不是行业红线,只是「秒抢」按本次条件推出的起步值——同一个数字放到别的系统身上未必安全,也未必需要留这么多余量。它至少取决于七件事:流量预估误差越大,水位目标要压得越低;冗余模型是 N+1 还是 N+2,决定同样峰值下还能不能把日常水位设得更高;故障域大小越大,一次故障吃掉的验证容量比例越高,水位目标要相应下调;扩容时延越短,越敢把水位推向上限;服务时间方差越重尾,越容易在峰值附近排队;降级能力越强,能优雅丢非核心流量的系统水位上限越高;恢复期追平负载越大,故障恢复后短时叠加到正常峰值上的积压请求越多。这七项没有一项是「秒抢」独有的行业常数,每次大促前都要重新核算。
两个压力测试说明为什么 0.67 仍需谨慎:峰值预测若高 15%,水位变成 138/180≈0.77;若故障让验证容量损失四分之一,只剩 135 万 QPS,水位变成 120/135≈0.89。容量规划要同时给正常峰值与故障后容量算账。
混沌工程不是随机破坏,而是四步可证伪的实验:
| 步骤 | 本次游戏日 | 缺了会怎样 |
|---|---|---|
| 1 · 稳态假设 | 下单成功率 ≥99.5%,P99 ≤500ms,库存对账差异为 0 | 实验结束后不知道系统算“扛住”还是“没报警” |
| 2 · 最小爆炸半径 | 一个订单单元、1% 流量、不触碰退款与对账 | 一次学习动作可能升级成整站事故 |
| 3 · 注入 | 下线单台缓存节点,观察回源、限流与依赖传播 | 没有受控变量,无法归因预案是否生效 |
| 4 · 自动回滚 | 成功率或 P99 连续越线即恢复节点并切走实验流量 | 停止实验依赖人工判断,爆炸半径随会议时间扩大 |
「没演练过的预案不是预案,是文档。」限流开关写在手册里,不等于它能在缓存节点消失时及时触发;多活拓扑画在架构图里,也不等于容量和数据边界真的能接管。实验的价值是让预案面对真实因果链。
本章不新增病历卡。收官总检要回答的不是“每张卡叫什么”,而是“游戏日先打哪一类”。No.01–14 各放入一个主类别:放大类关注“一个小触发怎样制造更多负载”,一致性类关注“多份真相怎样分叉”,单点类关注“共享瓶颈怎样让集群余量失效”。分类不是说卡片只有一种属性,而是为了决定先做哪种实验、稳态盯哪条曲线、事后怎样证明没留后遗症。
| 体质 · 卡片 | 共同机理 | 先打的实验 · 稳态看什么 · 怎样证明没留后遗症 |
|---|---|---|
| 放大类 No.04 05 06 07 08 09 11 |
小触发在系统内部长成比自身大得多的负载:并发回源、逐层重试、同步唤醒、沿依赖传播、按时间累积欠债。 | 注入一个小而可回收的触发——一次热点过期、一次下游超时、拖慢一个依赖、停一个消费者。稳态看内部负载 ÷ 入口负载有没有被放大,触发撤掉后曲线是否自己回落;回不落的就是死亡螺旋,靠限流与慢放量收场。后遗症看曲线是否回到稳态。 |
| 一致性类 No.02 03 12 13 14 |
同一份事实在时间或故障中被复制成两个版本:主与从、缓存与 DB、生产与消费、两个持有者、两个主。 | 故意让第二步失败或延迟——丢一次缓存失效、拉长复制延迟、重投一条消息、让临界区超过租期、隔离两地链路。稳态看对账差异笔数与并发写者数,性能大盘可能全绿,证据只在业务账上。后遗症看账能否对平,不看进程是否重启成功。 |
| 单点类 No.01 10 |
一处共享闸门或一个被钉住的子集先耗尽,其余节点的余量无法接力。 | 把一处打热或拖慢——注入慢查询、注入热点 key。稳态看最热 ÷ 平均与总容量余量的背离:集群平均健康、局部已经打满,就是单点。后遗症看把那一处摘掉或打散后,其余容量能否接住。 |
三类体质,三种爆炸半径。放大类的实验最容易越界,因为放大本身就是它的机理,先在预发验证“回落”是否成立;一致性类的实验对业务账动手,必须先备好对账脚本与补偿路径;单点类的实验半径最小,也最适合作为第一次生产游戏日的开场。
总检完成后,每张卡都应对应一个可执行实验:输入是什么、稳态看什么、爆炸半径多大、何时回滚、如何证明数据没有留下后遗症。附录图鉴是索引,游戏日计划才是处方。
今天这场游戏日注入的是可用性类故障——缓存节点掉线、依赖超时,坏了就降级,最坏结果是用户等得久一点。会后 Mia 问了下一个问题:数据一致性类的故障要不要也搬到生产上?跨机房复制中断、库存与订单对账偏离——这类实验一旦停止条件失效,赔上的不是可用性,是数据,而数据错了没有回滚按钮。
三个方案都对,差别在于:你需要多强的证据、组织能承担多大爆炸半径,以及这一类实验错了之后还能不能收回来。最后这个维度,正是可用性类实验和数据类实验的分水岭。
A · 只在预发:爆炸半径容易控制,可频繁验证注入工具、告警和回滚脚本,不影响真实用户。 账单:预发流量、数据量、依赖配额和网络拓扑常与生产不同,得到的是工具证据,不是生产稳态证据。适合团队刚起步、停止条件尚未自动化的阶段——也适合数据类实验的起步阶段:错了能重建环境,而不是去补生产账。
B · 直接生产:真实流量、真实依赖和真实容量共同参与,最容易暴露只在生产存在的耦合。 账单:未知项多,错误停止条件会把实验变事故。而且对数据类实验要说清楚一件事:自动化能做的是「停止注入 + 对旧写入 fencing + 切走流量」,它撤销不了已经写错的数据——所谓"自动回滚"在可用性类实验里成立,在数据类实验里只是止血。因此前置条件更硬:成熟单元化、自动停止与写入 fencing、事前就绪的审计 / 对账 / 修复流程、明确的 RPO,以及跨团队指挥。适合已多次演练同类故障、爆炸半径可硬隔离的组织——当某个耦合只在生产存在、预发怎么造都造不出来时,它是唯一能给出证据的选项。
C · 分级晋升:每一级都复用同一稳态假设、自动停止与写入 fencing 条件,以及对账与修复门禁;只有前一级证据通过才扩大真实度与范围。 账单:它不是免费的折中。数据类实验要走完这条晋升链,前提是先建出影子链路和对账门禁——这两样「秒抢」现在都没有,而它们本身就是季度级工程。在它们建成之前选 C,你实际拿到的只有预发那一级的证据,却付了 C 的治理成本。团队还可能在预发通过后迟迟不敢迈进生产,把分级变成拖延的正当理由。适合已具备影子链路与对账门禁、要把混沌工程做成长期能力的阶段。
「秒抢」的选择:按实验类别分开定。可用性类故障(就是今天注入的那类)继续走 C——它已经走到 1% 生产流量这一级,证据链完整,继续扩大范围即可。数据一致性类故障先按 A 停在预发,同时把影子链路和对账门禁排进下个季度;等这两样建成,再让它进入 C 的分级通道。同一个组织在同一时间对两类实验采用不同成熟度,这不是不彻底,而是承认「错了能不能收回来」这件事,本来就该改变你的风险偏好。只有当某个数据类耦合被确认只在生产存在时,才付 B 的代价,并把爆炸半径硬隔离到单个单元。
把上面判词里那句「先按 A 停在预发」的前提改掉:假设影子链路与对账门禁在这个季度已经建成并验收通过——生产写入能实时复制到独立环境做全量重放,对账差异超过阈值会自动挂起本轮实验,不需要人工去对账。数据一致性类实验,现在还应该继续停在预发吗?
翻转变量是「影子链路与对账门禁是否已经建成」。 原判词选 A 的唯一理由,就是这两样「现在都没有」——不是数据类实验本身危险,而是没有护栏时,C 的分级治理成本你已经在付,却只拿到 A 那一级的证据。这个事实一旦翻转,继续选 A 就从「谨慎」变成「过度保守」:影子链路能做全量重放,对账门禁能自动挂起,你已经有工具把「错了能不能收回来」这件事从「不能」变成「能,而且是机器判断的」。
正确答案变成 C,但要从最低一级重新起跑,不能直接搬到可用性类实验已经验证过的 1% 生产这一级——两类实验的历史证据不能互相借用,数据类实验的第一次生产暴露,仍然要重新走一遍稳态假设、最小爆炸半径与自动回滚。B 依然不成立:护栏建成不等于护栏已被验证,分级晋升的意义正是让新建的机制先在低成本环境里证明自己,跳过阶梯直接生产,等于让刚建好的对账门禁第一次上场就赌上真实用户数据。
回放第一次生产游戏日——顺序依旧是知识:先确认稳态,再注入,越线自动退,最后用数据决定是否扩大。
锁定稳态。记录成功率 99.93%、P99 268ms、库存对账差异 0;确认实验单元只承接 1% 流量,回滚脚本已预执行。
注入节点不可用。单台缓存节点下线,命中率在实验单元短降,互斥回源与网关配额生效;核心成功率最低 99.61%,P99 峰值 438ms。
验证自动回滚。人为将实验阈值临时收紧到 P99 400ms,系统自动恢复节点并切走实验流量;42 秒内 P99 回到 280ms,对账仍为 0。
记录证据,不追求“扛住”。团队确认回源保护有效,也发现一个非核心推荐接口共享了实验缓存池;修复隔离前,不扩大到第二个单元。
第 1 章说,后面每一章都在改那张基线图。现在把它与六年后的系统并排放回桌面:左边不是幼稚,右边也不是荣耀;它们分别是当时流量、团队与故障证据下,愿意支付的架构账单。
前面十六章,现场信息是别人给你摆好的:告警已经分好轻重,指标已经挑好该看哪几个,选项也已经写成 A/B/C/D 供你挑。这一次不是。下面这道题不给标准输入,只给一个新业务、一堆不完整甚至互相打架的证据,和两次真实会打断你计划的事件。它什么都不保存,刷新就没了——所以先把答案写在文本框里,写不出来也要写下卡在哪一步,那本身就是诊断。写完再点「展开基准答案」,对照自评清单给自己打分,而不是找一个「正确答案」。
「秒抢」中台孵化的潮玩品牌「波普工坊」今晚 8 点开一场限量手办直播发售,总量 5000 件,但不是一次性放出——主播每卖完一批,运营就临时申请追加,业务内部管这叫「realtime restock」。目前已经上了两批,直播间在线人数在 8 万到 35 万之间波动,每次主播口播「补货了」都会出现二次尖峰,具体倍数没人测过。你是这场直播抽调来的技术保障,PM 只甩给你下面这块屏幕,其余的要你自己去问、自己去假设、自己写下来。
注意:库存总量、每批具体数量、二次尖峰倍数、支付缺口的真实占比——这些数字都没给全,因为现场也没有全。要交这 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 分钟(诊断,为第 3 批做准备):用现有截图和日志估算「已发生」那批异常的量级下限;给第 3 批设一个明确的观察指标(支付回调缺口是否随本批扩大),而不是先动手扩容或限流。「先弄清楚问题往哪个方向移动,再决定往哪个方向治」本身就是及格线。
下面四段现场,只有一段和你有关。怎么定:把你在 E1 写的「第 1 分钟 / 前 10 分钟」里 最先真正改变系统行为的那一个动作(不是最先写下的那一句)归到下面四类之一—— 加容量走 X,收紧入口走 Y,改到达过程走 Z,只加观测不动参数走 W。 若你写了不止一项,按 X > Y > Z > W 取影响最大的那一项。 W 是 E1 基准答案推荐的做法,如果你照它做了,你就该走 W——这不是保守,是这道题认的答案。
四段都读完再挑。这正是这道题想练的事:同一个初始状态,选择不同,后面接到的证据也不同—— 而且不同分支拿到的证据强度并不相等,这本身就是要学的东西。
要交 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,运营那句「控太死」就不成立。
网络隔离后,两边写入曲线呈 ██‖██,两个单元都认为自己可写。最先要验证哪类护栏?
B。双线分叉是 No.14 脑裂,核心是限制写权限而不是提高吞吐。D 很诱人,因为用户会看到失败,但重试只会把冲突写得更多;先用多数派与 fencing 收回过期主的权限。
预估峰值 84 万 QPS,已压测验证容量 120 万 QPS。容量水位是多少,处于什么位置?
B。84 ÷ 120 = 0.70,已经到起步值,是否需要继续观察还要看误差、冗余与故障域这些具体条件。C 是最诱人的错误项,它把分子分母颠倒成 120/84;容量水位比较的是需求占验证容量的比例。
下面哪一组是混沌实验的正确顺序?
B。先定义可证伪的稳态,再硬限制影响范围,注入后由机器执行停止条件。A 很诱人,因为许多临时演练确实这样做,但注入后才定义成功标准,会让团队按结果修改题目。
健康态验证容量 180 万 QPS,故障后只剩 135 万;预估峰值仍是 120 万。为什么不能继续沿用健康态 0.67?
B。120/135≈0.89,健康集群的验证容量已经不是当前分母。A 是诱人的乐观假设,但故障不会替你减少用户需求;必须补容量或从入口减流。
合上书不等于练完。下面这条路径没有唯一顺序,但有一个默认节奏:先体检,再写预案,再拿真实故障验证预案,最后用计时面试逼自己在压力下把这套方法完整说一遍。每周都给了交付物、完成标准和建议时长——时长是给忙碌工程师的参考值,不是及格线。
四份模板可以直接复制去填:
第 1 周 · 系统体检 ================================ ① 请求链路(入口到最终写入,标出每一跳的协议与超时): ② 容量表(每一层:实测 QPS / 理论上限 / 当前水位 / 瓶颈猜测): ③ 前三风险(病历卡编号 + 触发条件 + 证据缺口——我现在还不知道什么): 1. 2. 3.
第 2 周 · 故障 Runbook(风险 #__) ================================ 症状(怎么发现): 稳态判据(正常时这几个数字应该是多少): 第一分钟动作(止血,机器可判断,不含"开会讨论"): 升级条件(什么时候喊人 / 切预案): 恢复步骤: 恢复证明(怎么证明系统和数据都没留后遗症,不是看着正常):
第 3 周 · 预发游戏日实验卡 ================================ 稳态假设(成功率 / 延迟 / 对账差异的具体数字): 最小爆炸半径(哪个环境 / 哪一部分流量 / 排除哪些操作): 注入内容(具体故障,一次只改一个变量): 停止条件(机器可判断的越线阈值): 恢复证明(实验结束后如何证明系统和数据都没有后遗症):
第 4 周 · 计时面试复盘表 ================================ 题型:□秒杀类 □非交易高读类 □异步协作类 时长:45 分钟 需求澄清用时:___ 分钟(参考 ≤5) 估算是否量化,且写出了假设:□是 □否 架构是否覆盖失败路径,不只是 happy path:□是 □否 是否主动说出权衡,而不是等面试官问:□是 □否 被追问后能否守住核心不变量:□是 □否 下一次要改进的一件事:
第 4 周的追问树、评分尺和计时 mock 题,见 附录 B · 面试映射总表——48 个高频追问是速查表,用来对照你在计时面试里漏答了哪一类。