5.1 大促峰值实战复盘 当我们把「接入削峰、缓存减负、路由提质」三件套讲完,最容易产生的错觉是:每一层都配好了,系统就稳了。但真实世界不会按你写的文档出牌。这一节我用一次真实感很强的大促复盘,把前四章的打法放进同一个时间轴,看它们在「QPS 从 5 万飙到 120 万」的那几十分钟里,到底是怎么咬合、又在哪里差点翻车的。 复盘的起点:不是「会不会崩」,而是「崩在哪里用户无感」 先说背景(量级为示意):某对话类产品,日常 QPS 约 5 万,大促零点峰值冲到 120 万,是日常的 24 倍。更麻烦的是,这 120 万不是均匀来的——零点前 3 分钟是一个陡峭的尖峰,紧接着是持续 20 分钟的高位平台,之后缓慢回落。特征是典型的「尖峰 + 长尾」。
当我们把「接入削峰、缓存减负、路由提质」三件套讲完,最容易产生的错觉是:每一层都配好了,系统就稳了。但真实世界不会按你写的文档出牌。这一节我用一次真实感很强的大促复盘,把前四章的打法放进同一个时间轴,看它们在「QPS 从 5 万飙到 120 万」的那几十分钟里,到底是怎么咬合、又在哪里差点翻车的。
先说背景(量级为示意):某对话类产品,日常 QPS 约 5 万,大促零点峰值冲到 120 万,是日常的 24 倍。更麻烦的是,这 120 万不是均匀来的——零点前 3 分钟是一个陡峭的尖峰,紧接着是持续 20 分钟的高位平台,之后缓慢回落。特征是典型的「尖峰 + 长尾」。
团队当时的目标很明确,不是「绝对不丢请求」,而是**「付费用户和高价值会话 0 感知,免费用户的轻微延迟可接受,绝不出现大面积 5xx」。这个目标的措辞很重要:它一开始就定了优先级**——不是所有流量平等,而是分层的。这正是第 2 章「限流哲学」里反复强调的:限流不是把人挡在门外,而是把有限资源先留给最关键的人。
零点前 3 分钟,连接数瞬间翻倍。如果按日常配置,单机 fd 和连接表内存会先被打爆——这是第 2 章讲过的「连得住」问题。当时接入层做了三件事:
这里值得停下来想一个问题:为什么是「排队」而不是「拒绝」?因为对话产品不是抢购秒杀——用户发一句「在吗」被拒了,他可能就走了,且不再回来。排队只是多等几百毫秒,体验上仍「活着」。这就是第 2 章说的「堵得住」的真谛:堵的不是用户,是洪峰的势头。
如果 120 万 QPS 全部真实打到模型,GPU 集群会瞬间被压垮——这根本不是加卡能解决的(你不可能 3 分钟内扩出 24 倍的卡)。这时候第 3 章的缓存就成救命层了。
复盘里最亮眼的一个数据是:语义缓存把模型实际调用量削减了约 35%。怎么做到的?大促期间大量请求是「活动规则问答」「优惠计算」「重复的热门话题」,这些语义高度重复的请求,即使措辞不同,命中缓存后直接返回,根本不进模型。再加一层「上下文压缩」——把长对话历史压缩成摘要再进模型,单次 token 消耗也明显下降。
但这里有个差点翻车的点:缓存的热点 Key。活动主会场的相关问答,几乎同一时刻被百万级请求击中,如果 Key 设计成「话题 + 时间戳」这种带随机量的,缓存根本命中不了;团队把它改成「话题 + 标准化语义向量哈希」,热点才真正被兜住。这和第 3 章讲的「缓存键设计」完全对得上——Key 设计错了,缓存就是摆设。
即便削峰 + 缓存之后,剩下的真实模型调用仍有几十万 QPS。这时第 4 章的路由上场。复盘里最实在的一个结论是:小模型承接了约 60% 的简单问题,GPU 成本下降约 40%,而用户侧感知的「答得变差」比例不到 2%。
为什么能这么省?因为大促流量里有大量「格式转换、活动规则、闲聊问候」类请求,小模型完全胜任。路由层按「难度 + 成本 + 延迟」三维,把这类请求稳定导给小模型,只把真正复杂的多步推理、代码生成留给旗舰。更关键的是合规硬约束:涉及金融、医疗的场景,路由直接锁地域、锁指定模型,优先级高于成本——这一点在峰值下也不能松动,否则就是合规事故。
但也暴露了一个坑:分类器在尖峰初期误判率短暂升高(因为流量结构突变,训练分布偏移)。团队的做法不是「硬扛」,而是给分类器加了一条「置信度低于阈值就走默认强模型」的兜底,宁可多花点钱,也不赌关键请求。这正是第 4 章说的「路由层自身不能成为单点,且要有失败默认链路」。
大促最怕的不是「压力大」,而是「压力大 + 某供应商恰好挂了」。复盘当晚,主供应商其中一个区域确实出现了抖动。但因为第 4 章编排层早就配了多供应商容灾 + 总预算护栏,流量在秒级切到备用供应商,用户侧只是「某条回答稍慢」,没有中断。
值得强调的是「总预算护栏」:切换供应商不是为了无限兜底,而是设了成本上限——如果备用供应商价格过高,策略会自动收敛到「只保核心链路」,非关键请求允许排队甚至降级,而不是无脑全量切过去把账单也冲爆。这是把「可用性」和「成本」放在同一架天平上权衡,而不是顾一头。
把上面四层放在同一张图里,你会发现它们的关系不是「串联」,而是「层层减负 + 各自兜底」:
每一层都在为下一层减负,同时每一层都有自己的失败默认链路。这才是「协同」的真正含义——不是谁依赖谁,而是谁都能在别人出问题时兜住。
诚实复盘,必须区分这两种。
可复制的(请立刻沉淀成清单):
只是运气的(不能当经验):
我的建议是:把「可复制」那四条写成你的大促作战清单,每半年做一次故障演练(故意掐掉一个供应商、故意放大尖峰),验证兜底链路真的通,而不是纸面通。
5.1 讲了接入层「排队而非拒绝」,但排队不是无限的。尖峰持续 20 分钟,如果用户的请求在队列里排了 10 秒还没出去,体验同样会崩。所以接入层还必须配「排队超时」:超过阈值的请求,要么返回「系统繁忙,请稍后再试」的友好态,要么降级到一个极简应答(比如「当前咨询量大,已为你预约,稍后回复」),而不是让用户对着转圈圈干等。
这背后是一个被很多人忽视的命题:延迟也是一种拒绝,只是更温柔。你用排队换来了「用户没被拒」,但排队太久本质还是把用户推走了。所以排队上限、超时阈值、降级话术,三者必须一起设计,不能只配令牌桶就完事。大促复盘里,团队把免费用户的排队上限设为「不超过 800ms 自动降级」,换来的就是「虽然慢,但每次都有回应」的体感——这比「一半请求转圈超时」好太多。
最后,把前面四层的收益合并成一张总账,你会更清楚「协同」到底值多少钱(量级为示意,用于讲逻辑):
| 层 | 动作 | 量化收益(示意) | 用户侧感知 |
|---|---|---|---|
| 接入层 | 优先级排队 | 付费用户 0 感知 | 免费用户多等 200~500ms |
| 缓存层 | 语义缓存截胡 | 模型调用 -35% | 无(命中即返回) |
| 路由层 | 小模型承接 | GPU 成本 -40% | 变差 <2% |
| 容灾层 | 多供应商切换 | 故障 0 中断 | 偶发稍慢 |
这张表也是你向老板、向业务方证明「架构投入值得」的最硬材料:每一层的收益都能被量化,叠加起来就是「扛住 24 倍峰值 yet 成本可控」。我反复说「用数字说话」不是客套,而是因为架构的价值,只有被量化成总账,才会在资源争夺战里活下来。
如果你也要扛一次类似峰值,这张清单可以直接拿去改:
到这里,5.1 的复盘就完整了:从接入层的优先级排队,到缓存的热点兜底,到路由的置信度兜底,再到容灾的成本护栏,最后补上排队超时的预期管理——六个动作构成一张可复制的大促作战清单。
小结:这一节的复盘想说的不是「我们多厉害」,而是「分层架构在真实洪峰下是怎么咬合的」。接入削峰、缓存减负、路由提质、容灾兜底,四层各自减负、各自兜底,才让 120 万 QPS 从「系统噩梦」变成「可管理的流量」。下一节,我们把视角拉远,看扛住之后,下一步该往哪里演进。