10 万人抢 50 台手机的晚上
晚上八点整,活动页开放下单。八点零三分,监控曲线齐刷刷变红:数据库 CPU 冲到 100%,下单接口 P99 从 200ms 飙到 30 秒,支付回调堆积。八点十五分人工止血——活动下线。对完账更糟心:50 台手机卖出去 82 台,超卖 32 台;客服后台挤满了付了钱没订单的用户。一场准备了三周的活动,正式开抢三分钟就结束了。
复盘会上把链路摊开一看,问题没有一处是"没考虑到",全是"没想过会这样":库存就是一行数据,10 万个请求在那一秒挤着 UPDATE 同一行——锁竞争直接把数据库打满;下单逻辑同步走完扣库存、建订单、锁优惠,每个请求都在数据库里走全程;前端一点防护没有,脚本比人手快。常规架构的每一层,都被同一个瞬间压在了同一处。
秒杀的三重属性
秒杀难,不是因为流量大——大促的峰值流量可能更大——而是三个属性叠加:瞬时洪峰——流量不是慢慢涨上来的,是开抢那一秒从零直接跳到峰值,没有预热缓冲;稀缺热点——所有流量都指向同一件商品的同一行库存,热点集中到极致,不是分散的读热点,是同一行的写热点;作弊对抗——黄牛脚本的请求和真人混在一起,每个防护漏洞都会被自动化地利用,防护做得再好也挡不住有人在研究你。
为什么常规架构扛不住
把常规下单链路放进秒杀场景,每一层都在裸奔:入口层——10 万请求原样直达应用,应用扩容再快也接不住瞬时倍数的洪峰;服务层——下单逻辑同步串行执行,扣库存那行 UPDATE 的行锁让所有请求排队,吞吐量瞬间坍塌;数据层——查库存、扣库存、建订单、记流水,每个请求四次数据库往返,10 万请求就是 40 万次访问同一批数据。三层叠起来,崩溃不是概率问题,是必然事件。
// 崩溃当晚的下单逻辑:每个请求都走到 DB 最深处
@Transactional
public Order create(CreateOrderReq req) {
Stock stock = stockMapper.selectForUpdate(req.skuId); // 行锁:10 万人排这一行
if (stock.num < req.num) throw new BizException("sold out");
stockMapper.deduct(req.skuId, req.num); // 热点行 UPDATE
orderMapper.insert(build(req)); // 建单
couponMapper.lockAndUse(req.couponId); // 再一次锁竞争
return order;
}
// 问题不在代码写得差,在每一层都没有过滤与缓冲好消息:这是一道漏斗题
换个视角看问题:10 万个请求抢 50 件商品,99.95% 的请求注定失败——系统的核心任务不是满足它们,而是让它们体面地、便宜地失败。失败的越早越便宜:CDN 层拦下一个请求几乎零成本,数据库层拦下一个请求用的是最贵的锁竞争。所以秒杀架构的主线只有一条:建一道层层收窄的漏斗,让绝大多数请求在便宜的层就止步。这一系列十五篇,就是沿着这条漏斗,从最外层一路讲到最内层:前端削峰、网关防刷、风控对线、库存预扣、异步下单、对账兜底,最后压测验收。下一篇先搭漏斗的骨架:流量漏斗的分层设计。
评论 (0)