国庆期间小城景区被流量砸中,一天从几千人涨到几万人。跳变负载下,票务系统最先出问题的不是页面,是库存 ------超卖、有票下不了单、退票不释放,全部指向同一个根因:把"库存"当成了一个可以随便加减的数字。
这篇讲清楚三件事:库存怎么扣才不超卖、候补队列怎么设计才不丢单、断网时怎么保证最终一致。

一、超卖的根因:扣减和校验不是原子的
最朴素的写法是"先查再扣":
```
SELECT stock FROM inventory WHERE sku=?; -- 查
if stock > 0:
UPDATE inventory SET stock = stock - 1; -- 扣
```
单机、低并发下没问题;开票瞬间请求集中度上来,两个请求同时查到 `stock=1`,都判定可下单,超卖就发生了。
超卖不是算错了库存,是"校验"和"扣减"之间留了一道缝。 并发越高,这道缝越致命。
三种常见修法,各有取舍:
| 方案 | 做法 | 优点 | 代价 |
|------|------|------|------|
| 数据库悲观锁 | `SELECT ... FOR UPDATE` | 实现简单、强一致 | 高并发下锁等待严重,吞吐低 |
| 数据库乐观锁 | `UPDATE ... WHERE stock=旧值` | 无锁等待 | 冲突高时大量重试,成功率下降 |
| 缓存原子扣减 | Redis `DECR`/Lua 脚本 | 高吞吐、原子 | 需处理缓存与 DB 的一致性 |
大客流场景下,缓存原子扣减是主力:库存预热到 Redis,用 Lua 脚本把"校验+扣减"合成一个原子操作;扣减成功后异步落库,落库失败走补偿。
二、把洪峰拆掉:分时、分批、队列
防超卖的核心不是把库存扣得更快,而是让开票瞬间不用抢。 比把扣减优化到极致更有效的,是让洪峰根本不必集中出现。三个手段叠加:
-
分时预约:把一天的库存按时间片切开,开票请求从"集中在 0 点"变成"摊到各个时段"。
-
批次释放:库存不是一次性放完,而是按批次(如每 5 分钟一批)释放,把瞬时请求拉平。
-
队列缓冲 :下单请求先进队列,按处理能力消费;超出能力的请求进候补而不是直接失败。

三、候补队列:不是"补票",是缓冲池
很多人把候补理解成"卖不出去的票补一刀",其实它的真正价值是给洪峰一个缓冲池。
设计要点:
- 顺序性:候补必须严格 FIFO,否则用户会质疑公平性------用 Redis `ZSET`(score=入队时间戳)或带序号的队列。
- 释放时机:退票、超时未支付、批次释放三类事件都会产生可售库存,统一走"库存释放 → 触发候补消费"。
- 通知与超时 :候补命中后给用户一个支付窗口(如 15 分钟),超时未支付则释放给下一位------这一步必须有定时任务兜底,否则库存会被"占住不放"。
- 幂等:同一个候补请求重复消费只能成功一次,否则会重复出票。
四、幂等与防重:同一笔订单只能成功一次
大客流下用户会疯狂点"提交"。防重的核心是幂等键:客户端生成带时间戳的 `requestId`,服务端用 `SETNX` 或去重索引拦截重复请求。
```
key = "order:idem:" + userId + ":" + requestId
if not redis.set(key, 1, nx=True, ex=600):
return 已有订单 # 重复请求,直接返回
```
配合数据库层 `order_no` 去重索引做最终防线,双保险。
五、断网兜底:本地闭环 + 最终一致
小城景区的网络条件先天不足,旺季丢包抖动是常态。大客流系统真正的考验,不是人多的时候快不快,而是网断的时候乱不乱。
做法是"本地闭环 + 最终一致":
- 票证数据在闸机侧留存一份(本地 SQLite/文件),断网照常核销;
- 断网期间的销售走本地库存预扣,记录操作日志;
- 网络恢复后,本地日志与中心对账同步,冲突按"先到先得 + 人工复核"处理。
这里的关键是可对账:每一笔离线操作都要有可区分的标识和时间戳,恢复后能重放、能比对、能定位差异。

六、取舍清单
| 问题 | 建议做法 | 注意 |
|------|---------|------|
| 防超卖 | Redis Lua 原子扣减 + DB 去重索引 | 缓存与 DB 一致性要有补偿 |
| 削峰 | 分时预约 + 批次释放 | 分片粒度太细会伤转化 |
| 候补 | ZSET 严格 FIFO + 支付超时释放 | 定时任务兜底,否则库存被占死 |
| 防重 | 幂等键 + 去重索引 | 幂等窗口期要覆盖重试周期 |
| 离线 | 本地闭环 + 操作日志 + 对账 | 可重放、可比对、可定位 |
系统能不能扛住洪峰,不取决于单点做得多快,而取决于每个环节都留了兜底。
*这是《景区票务实战》系列第 3 篇,上一篇讲大客流的三本账,下一篇拆解「分时预约怎么设计才能真正削峰」。*
*技术判断基于票务系统设计与落地经验;数据引用:开封万岁山景区公告(10-04)、去哪儿《2026 国庆景区热度报告》(10-05)。*