🧩 典型题干
场景:春运开售瞬间高并发查询与下单。
要求:余票准确(尽量不超卖)、下单与支付可恢复、退改签一致、热点车次不把系统拖死。
💡 核心难点:余票是“强竞争资源”,还要支持站点区间票(A->B、A->C 等)与锁座超时释放。
📚 学习正文:抢票系统如何保证余票准确且扛住热点?
交互图:抢票关键链路闭环(点步骤看核心设计点)
面试时最怕说成“库存-1”。这里的关键是:余票=区间可用性;锁座要做到原子校验+占用,并且要有超时回收+对账。
1) 查余票抗压:读多写少的典型热点场景
- 多级缓存:时刻表/站点信息可走 CDN;余票聚合结果走 Redis。
- 热点保护:对热门车次 key 做互斥更新/逻辑过期,避免击穿。
- 降级兜底:极端高峰允许返回“余票可能不准/请稍后刷新”,但锁座必须强一致。
2) 余票建模:为什么不是简单的 stock-1?
- 区间票:同一座位可被多个区间共享(A→B 占用不影响 B→C)。
- 核心判断:座位在 [start,end) 段是否全部空闲(区间无交叠)。
- 常见实现:区间段占用数组/位图(bitset)表示占用,支持快速 AND/冲突判断。
3) 锁座并发控制:写少但必须“原子校验 + 占用”
- 分片串行:按 trainId-seatType-date 路由到同一分片,分片内串行锁座降低冲突。
- 原子锁定:Redis Lua/DB 短事务:检查区间空闲 → 写占用 → 生成 lock_order。
- 幂等:用户+车次+出行日+区间 作为幂等键,避免重复锁座。
4) 支付与出票:用状态机承接重试与回调
- 支付幂等:支付回调可能重复,状态变更要幂等。
- 出票异步:支付成功后发 MQ 触发出票,避免同步链路过长。
- 可追溯:订单/锁座记录保留状态流转与原因码,便于排障与对账。
5) 超时回收:占座不支付要自动释放
- 延迟队列:锁座成功发送延迟消息(T+10min),到期检查是否已支付,否则释放。
- 定时兜底:定时任务扫描超时 lock_order,做幂等回收。
- 回收幂等:重复回收不会破坏占用位图(基于版本/状态判断)。
6) 对账补偿:让系统“最终一致且可自愈”
- 对账场景:占用存在但订单不存在/订单失败、支付成功但未出票等。
- 补偿方式:重新触发出票/回收占用/修复订单状态(都要幂等)。
- 产出指标:异常单比例、修复时延、回收成功率。
1) 需求澄清(面试先问)
- 票种与席别:硬座/软座/一等座等,是否支持选座。
- 区间票:余票按“车次-区间-席别”扣减还是更细粒度。
- 下单流程:是否允许“占座 10 分钟待支付”?超时如何回收。
- 一致性要求:查询余票必须强一致吗?允许短暂不一致吗?
2) 量级与挑战
热点车次:千万级请求同时查余票/锁座
区间票:同一座位被多个区间共享
锁座超时:占座未支付需要自动回收
高并发读:余票查询是读多写少场景
3) 数据模型:车次、站点、座位、占用
- 车次站点序列:train_station_seq(车次、站点序、站点ID)。
- 座位表:seat(座位ID、车次、席别、车厢、座位号)。
- 占用表:seat_occupancy(座位ID、起始站点序、终止站点序、订单ID、状态)。
- 锁座订单:lock_order(订单ID、用户、座位ID、区间、状态、过期时间)。
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) 稳定性治理
限流排队:热点车次限流、排队保护
降级策略:余票查询降级、锁座降级
监控告警:锁座成功率、支付超时、余票一致性
演练:锁座风暴、支付失败演练