题目重点:定义场次座位库存与订单状态机,处理并发抢座、暂锁过期、支付回调重试和退款。
题意与假设
这道练习题保留了面经中值得训练的核心约束,并将需要澄清的条件单独列出。现场拿到变体时,先和面试官确认这些条件。
- 训练版用户选具体场次和座位,座位在该场次只售一次;不对不同场次共享座位库存。
- 座位先有短期 hold,再付款出票;hold 时长和支付失败策略由业务设定。
- 支付回调可重复或延迟,外部支付结果需对账;假设出票后可退款,但退票规则题目未明确。
解题路线
- 实体分 Theater、Auditorium、Seat、Show、ShowSeat、Hold、Order、Ticket;ShowSeat 的唯一键是 (showId,seatId),不能只按物理 seatId 锁。
- 持座操作在事务中检查 ShowSeat 为 AVAILABLE 或已过期 HOLD,条件更新为 HELD(owner,expiresAt,version);同座并发只有一个条件写成功。
- 订单从 CREATED→HELD→PAYMENT_PENDING→CONFIRMED/FAILED/EXPIRED;超时 worker 回收 hold,但以版本/owner 检查,防旧定时任务释放新用户的锁。
- 创建支付 intent 和处理回调都用 orderId/支付事务 ID 幂等;付款成功但 hold 已失效时不能盲出票,要走退款或补偿并告知用户。
- 出票写唯一 ticketId 和座位归属,发布通知经 outbox;查询座位图可用缓存但最终占座必须经过权威条件写。
- 热点首映按 showId 分片或队列化抢座,读缓存快速展示、权威库处理争抢;指标看锁冲突、过期回收延迟、支付后出票失败和对账差异。
手动走一遍
两用户同时抢同场次 A 排 5 座,条件更新只让一个获得 hold。该用户在 5 分钟后过期,旧释放任务带 version=3;另一用户重获 version=4 后,旧任务不得把 version=4 的 hold 释放。若支付回调重复两次,只生成一张票。
规模与取舍
S 个场次座位需要 O(S) 库存状态;一次锁座/出票预期按唯一键 O(log S) 索引定位,批量选 k 座需一个事务内检查 k 个条目,避免部分成功。实际峰值受热门场次读写集中度与支付 SLA 影响。
易错点与追问
容易失分的边界
- 缓存显示空位就直接出票,没有原子占座。
- 座位唯一性只按物理座位 ID,导致不同时段冲突。
- 过期任务不校验版本,释放了后来的合法 hold。
- 支付成功与出票失败之间没有补偿路径。
- 重复支付回调重复出票或重复扣款。
继续深挖
- 如何保证同一订单选择的相邻 k 个座位要么全锁成功,要么全失败?
- 场次被取消后已付款票怎样批量退款与通知?
- 黄牛高频抢座与普通用户如何公平?
- 弱网下用户看到过期倒计时与服务端时间不一致怎么办?
