← 返回面试专题

🚄 12306 抢票系统如何设计?

核心目标:余票一致性、超卖控制、热点车次抗压、支付与退改可追溯

🧩 典型题干

场景:春运开售瞬间高并发查询与下单。 要求:余票准确(尽量不超卖)、下单与支付可恢复、退改签一致、热点车次不把系统拖死。
💡 核心难点:余票是“强竞争资源”,还要支持站点区间票(A->B、A->C 等)与锁座超时释放。

📚 学习正文:抢票系统如何保证余票准确且扛住热点?

交互图:抢票关键链路闭环(点步骤看核心设计点)

面试时最怕说成“库存-1”。这里的关键是:余票=区间可用性;锁座要做到原子校验+占用,并且要有超时回收+对账

1) 查余票抗压:读多写少的典型热点场景

  • 多级缓存:时刻表/站点信息可走 CDN;余票聚合结果走 Redis。
  • 热点保护:对热门车次 key 做互斥更新/逻辑过期,避免击穿。
  • 降级兜底:极端高峰允许返回“余票可能不准/请稍后刷新”,但锁座必须强一致。

1) 需求澄清(面试先问)

2) 量级与挑战

热点车次:千万级请求同时查余票/锁座 区间票:同一座位被多个区间共享 锁座超时:占座未支付需要自动回收 高并发读:余票查询是读多写少场景

3) 数据模型:车次、站点、座位、占用

4) 关键链路:查询余票 → 锁座 → 支付 → 出票

1) 查询余票:按车次-区间-席别聚合可用座位 2) 锁座:原子写入占用记录(乐观锁/分片串行) 3) 支付:支付成功后更新订单状态为“已支付” 4) 出票:生成正式票,释放锁座记录 5) 超时回收:定时任务扫描超时锁座并释放

区间票余票模型示意(位图/区间段思想)

站点序列:S1 --- S2 --- S3 --- S4 --- S5 区间段编号: 1 2 3 4 一个座位的占用位图(1=占用,0=空闲):b1 b2 b3 b4 例:当前占用 S1->S3 (占用段 1、2) 位图:1 1 0 0 此时请求: - S3->S5(段3、4)可以卖(与占用不重叠) - S2->S4(段2、3)不能卖(段2冲突) 锁座动作本质:原子判断 [start,end) 段全为0 → 再把这些段置1
💡 工程取舍:真实系统常把“位图/占用段”放在缓存或专用服务里(便于高并发判断),最终仍以主库的占用记录/订单状态做对账兜底。

系统架构图(面试时用它讲全链路)

用户/APP/小程序 | v CDN/静态化(时刻表/站点信息/活动页) | v 网关/API Gateway(限流/验证码/黑名单/动态路由) | +------------------------------+ | | v v 查询链路(读多) 下单链路(写少但强一致) | | v v 余票查询服务 锁座服务(原子锁定) | | +--> Redis 多级缓存 +--> 座位占用/锁座订单(主库) | - 热点Key保护 | - 分片串行(车次-席别) | - 逻辑过期/互斥更新 | +--> 只读副本/搜索索引(可选) +--> MQ 事件(锁座成功/支付成功/退改签) | v 订单服务(状态机/幂等) | +--> 支付服务(幂等/回调) +--> 出票服务(生成车票/通知) +--> 超时回收(延迟队列/定时任务) +--> 对账补偿(周期扫描修复异常) 观测与治理:热点车次限流排队、锁座成功率、回收延迟、余票一致性、队列堆积、P95/P99 告警

5) 抗压与一致性设计

6) 稳定性治理

限流排队:热点车次限流、排队保护 降级策略:余票查询降级、锁座降级 监控告警:锁座成功率、支付超时、余票一致性 演练:锁座风暴、支付失败演练

🎯 面试题(建议学完上面正文再做)

1. 需求澄清:你会问什么?简单
  • 票种与席别:硬座/软座/一等座等,是否支持选座。
  • 区间票:余票按“车次-区间-席别”扣减还是更细粒度。
  • 下单流程:是否允许“占座 10 分钟待支付”?超时如何回收。
  • 一致性要求:查询余票必须强一致吗?允许短暂不一致吗?
2. 余票数据模型怎么设计?(关键)中等

一种常见建模方式:

  • 车次站点序列:S1,S2,...,Sn。
  • 席位资源:具体座位(如 1 车厢 01A)在多个区间共享。
  • 占用表示:用区间位图/区间段占用数组来判断座位是否可用。
💡 解释要点:“余票”不是简单的 stock-1,而是“某个座位在某个区间是否可用”的判定与锁定。
3. 下单(锁座)如何避免超卖?困难
  • 串行化关键资源:按“车次”或“车次-席别”做分片,把锁座写操作路由到同一分片串行处理。
  • 短事务锁定:锁座写入座位占用(或余票扣减)必须是原子操作(DB 行锁/Redis Lua)。
  • 锁座 TTL:生成“锁座订单”状态=PENDING_PAY,设置超时任务回收。
⚠️ 面试高频追问:如果锁座回收失败怎么办?答:对账/补偿任务周期性扫描超时订单,进行幂等回收。
4. 查询余票如何抗压?(读多写少)困难
  • 多级缓存:时刻表/站点信息走 CDN+本地缓存;余票走 Redis 缓存。
  • 热点保护:热门车次 key 做互斥更新/逻辑过期,避免击穿。
  • 读写分离:查询走缓存/只读副本;写(锁座)走主链路。
5. 面试 2 分钟总结怎么讲?中等
  1. 读写分治:查询大量走缓存与静态化,写操作(锁座)做分片串行。
  2. 余票模型:按区间/座位占用建模,锁座原子更新避免超卖。
  3. 支付与回收:锁座订单超时回收 + 对账补偿,保证最终一致。
  4. 稳定性:限流/排队/降级,热点车次保护与告警。