抢票系统怎么设计才不超卖?Awesome Architecture案例StarArena完整推演(虚拟等候室+锁座+支付状态机) 抢票系统怎么设计才不超卖Awesome Architecture案例StarArena完整推演虚拟等候室锁座支付状态机【免费下载链接】awesome-architecture Architecture-first system design: 26 bilingual tutorials, 25 architecture templates, and 6 end-to-end cases covering distributed systems, AI-native systems, RAG, coding Agents, and production trade-offs.项目地址: https://gitcode.com/gh_mirrors/awesomearc/awesome-architectureAwesome Architecture是一个专注「架构」而非「代码」的开源知识库收录了 26 篇双语教程、25 张架构模板和 6 个端到端案例。其中的StarArena 演唱会抢票系统案例把「100 万人抢 2 万张票」这个最尖的压力场景完整推演了一遍从为什么必须上虚拟等候室到锁座预占 超时释放如何防止超卖和占座再到支付状态机 幂等 对账补偿怎么兜住回调迟到、重复、丢失的坏情况。本文带你用 10 分钟读懂这套抢票系统架构设计。 抢票为什么不是普通下单先算一笔入口账普通电商库存不够可以补货演唱会座位是天然有限库存——2 万个座位卖多一张就是事故普通浏览高峰页面慢一点可以忍抢票的高峰是同一秒的瞬时尖刺。StarArena 案例先做了一笔很「要命」的粗算维度数量级场馆座位20,000 个预约提醒人数1,000,000 人开售前 10 秒实际点击约 300,000 次用户平均重试次数2 次入口抢票请求≈ 60,000 req/s真正能买到票的人≤ 20,000这笔账的结论很反直觉98% 以上的请求注定买不到票所以不能让它们全部打进最贵、最脆弱的核心链路。架构重心由此确定先挡流量再谈下单先控资格再谈库存先保证可恢复再谈体验顺滑。 这是抢票系统的第一性原理把洪峰挡在门外远比在门内拼命加机器有效。 虚拟等候室如何扛住开售瞬间的 6 万 QPS旧路径的问题三个动作挤在一条链路上早期 StarArena 卖的是小型 Livehouse 演出单体应用完全够用。但顶流开售后架构立刻暴露出裂缝触发信号表现入口洪峰过尖10 秒内 60,000 req/s 直接打到抢票接口热门票档成热点同一票档被反复争抢库存扣减集中到少数记录数据库锁等待飙升下单 P99 从几百毫秒变成数秒甚至超时支付回调乱序/丢失用户付了钱订单还显示待支付核心矛盾在于开售按钮背后其实是三个完全不同的动作——争资格、锁库存、收钱出票。早期单体把它们揉在一条同步链路里小流量下没问题洪峰下必然被撕开。新架构所有请求先进「虚拟等候室」演进后的抢票主链路分成清晰的几段用户 → CDN/活动页 → 虚拟等候室(发令牌) → 选票/锁座入口 → 座位库存服务(锁座/释放) → 订单状态机(待支付) → 第三方支付 → 出票服务 → 对账/补偿任务各段的职责边界非常清楚CDN活动页静态内容尽量不进入核心系统虚拟等候室所有人先排队按系统真实承受力一批批发放一次性放行令牌把洪峰「整形」成细水长流选票/锁座入口校验令牌、用户资格和防刷规则库存服务只管座位的锁定、释放、确认用原子操作扣减绝不超卖订单状态机承认支付和出票不会一次成功用状态推进而非「赌一次同步调用」。⚠️ 常见误区把虚拟等候室理解成「一个排队页面」。它的灵魂不是让用户等待而是准入控制——令牌不是订单拿到令牌才允许进入最脆弱的锁座链路。 票到底什么时候扣三种扣减时机方案对比这是整个案例最重要的决策库存扣减时机决定了订单、支付、出票的整个结构。方案流程优点代价A · 支付成功后扣票下单 → 支付 → 扣票 → 出票没付款不占库存座位利用率高可能出现「支付成功但票没了」退款投诉事故B · 下单时直接扣票下单成功 正式扣票 → 等支付逻辑直观不易超卖大量用户不支付会长期占票黄牛可恶意占座C ·锁座预占 超时释放抢资格 → 锁座(15 分钟) → 待支付订单 → 支付成功正式出票超时未支付自动释放防超卖也避免未支付订单永久占票状态机更复杂要处理超时、回调、补偿StarArena 选择方案 C因为它匹配三条硬约束票不能超卖、支付可能失败、用户不能无限占座。一句话记住这个模式「占用 超时释放」是所有「临时持有稀缺资源」场景的通用解——订座、库存预占、分布式锁都是它。防超卖本身还有个关键细节普通「查余量 → 减余量 → 写回」在高并发下必然超卖两个请求都读到剩 1 张各扣一张。正确做法是用原子操作「扣减并返回结果」只有一个请求能把最后一张减成 0另一个拿到「不足」。在线票务/抢票模板 里还有更进一步的手段热点库存放内存原子扣减、必要时分段库存把 1000 张拆成 10 段各 100 张分散竞争。⚙️ 支付状态机回调迟到、重复、丢失都怎么兜抢票系统最危险的不是慢而是慢的时候还把票和钱弄错。第三方支付是外部系统回调可能延迟、重复、丢失所以支付链路必须「为失败而设计」机制解决什么问题订单状态机明确规定「待支付 → 已支付 → 已出票 / 已关闭」状态不能乱跳幂等推进同一个支付回调重复来几次结果也只算一次已处理直接返回成功状态条件更新超时任务只在订单仍是「待支付」时才能关单支付回调只在「待支付/支付确认中」时才能推进——靠条件保护而不是靠时间猜主动查单回调没来就定期主动问支付平台不依赖「别人一定会通知我」对账 补偿定时比对订单、支付、出票三方状态发现卡住如「已支付未出票」自动补推一个最刁钻的竞态用户在第 14 分 59 秒支付回调第 15 分 02 秒才到和超时释放任务撞车。靠「时间」判断必错靠状态版本号 条件更新才能保护两边只有一个能赢另一边失败后由对账任务修复。 幂等、Saga、对账这些手艺的系统讲法见教程 11 · 数据一致性工程 和 12 · 为失败而设计支付侧的账本与复式记账设计见 支付系统架构模板。 坏了怎么办六类故障场景的兜底设计抢票系统的成熟度不看成功路径多漂亮而看坏情况能不能被系统自己发现、自己推进、自己修回来故障直接后果架构兜底等候室发令牌过快锁座链路被打爆监控 P99 与令牌消耗速度动态降低放行速率用户锁座后不支付好座位被占住15 分钟超时扫描自动释放座位支付成功但回调丢失用户扣钱、订单仍待支付主动查单 支付对账幂等补推状态继续出票支付回调重复订单被重复推进回调幂等键 支付单唯一索引出票服务短暂故障已支付但没拿到票订单进入「待出票」服务恢复后补发释放与回调撞车误关已支付订单条件更新 对账修复 延伸阅读如何读这个仓库的对应资料本案例不是重写票务模板而是把模板里最危险的一条链路拿出来细推。推荐读法先读案例再回看模板你会更容易看懂「虚拟等候室」为什么是灵魂部件而不是可有可无的排队页。资料相对路径读什么⭐ StarArena 案例正文cases/stararena-ticketing/README.md完整推演入口账 → 触发信号 → ADR 决策 → 数据流 → 故障兜底️ 在线票务/抢票模板templates/online-ticketing/README.md虚拟等候室、原子扣减、锁座超时的架构地图与常见反模式 支付系统模板templates/payment-system/README.md幂等、状态机、对账、复式记账 电商平台模板templates/ecommerce-platform/README.md商品/订单/库存/支付的基本关系 方法论教程08 · 架构决策记录与演进案例中 ADR-01/02/03 的记录方式 案例总览cases/README.md6 个案例怎么配合模板与教程读✅ 五句话总结旧架构不是错约束变了才需要演进——小活动单体合理顶流开售把它逼到边界先算入口账再画架构图——6 万 req/s 里 98% 注定失败逼出虚拟等候室抢票要拆三段——抢资格、锁库存、收钱出票每段才能限流、超时、重试、补偿锁座预占是拿复杂度换正确性——避免「钱扣了没票」也用超时释放避免长期占票支付成功不是结束而是状态推进的开始——没有幂等、状态机和对账补偿系统迟早要人工救火。【免费下载链接】awesome-architecture Architecture-first system design: 26 bilingual tutorials, 25 architecture templates, and 6 end-to-end cases covering distributed systems, AI-native systems, RAG, coding Agents, and production trade-offs.项目地址: https://gitcode.com/gh_mirrors/awesomearc/awesome-architecture创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考