大客流票务系统怎么防超卖:库存并发、候补队列与幂等设计的工程取舍

国庆期间小城景区被流量砸中,一天从几千人涨到几万人。跳变负载下,票务系统最先出问题的不是页面,是库存 ------超卖、有票下不了单、退票不释放,全部指向同一个根因:把"库存"当成了一个可以随便加减的数字。

这篇讲清楚三件事:库存怎么扣才不超卖、候补队列怎么设计才不丢单、断网时怎么保证最终一致。

一、超卖的根因:扣减和校验不是原子的

最朴素的写法是"先查再扣":

```

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 脚本把"校验+扣减"合成一个原子操作;扣减成功后异步落库,落库失败走补偿。

二、把洪峰拆掉:分时、分批、队列

防超卖的核心不是把库存扣得更快,而是让开票瞬间不用抢。 比把扣减优化到极致更有效的,是让洪峰根本不必集中出现。三个手段叠加:

  1. 分时预约:把一天的库存按时间片切开,开票请求从"集中在 0 点"变成"摊到各个时段"。

  2. 批次释放:库存不是一次性放完,而是按批次(如每 5 分钟一批)释放,把瞬时请求拉平。

  3. 队列缓冲 :下单请求先进队列,按处理能力消费;超出能力的请求进候补而不是直接失败。

三、候补队列:不是"补票",是缓冲池

很多人把候补理解成"卖不出去的票补一刀",其实它的真正价值是给洪峰一个缓冲池。

设计要点:

  • 顺序性:候补必须严格 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)。*

相关推荐
杨云龙UP31 分钟前
生产实战:Keepalived + Nginx + MySQL 双向复制高可用架构部署与原理详解
运维·数据库·mysql·nginx·架构·keepalived·高可用架构
数字营销观察41 分钟前
geo管理软件选型指南:从技术架构到场景落地的核心考量
架构
青峰讲运维1 小时前
OpenStack Nova 计算服务深度解析|原理 + 架构 + 完整实操
架构·openstack
深入云栈1 小时前
Netty 4.2.x 源码深度解析 (二十一):Pipeline 事件传播 —— Inbound 与 Outbound 的双向流转
java·后端·架构
FYKJ_20101 小时前
springboot高考志愿填报推荐系统14332-计算机课程设计、毕业设计
javascript·spring boot·python·架构·django·课程设计·高考
找了一圈尾巴2 小时前
Agent 架构的记忆系统:从滑动窗口到离线整合,一套分层记忆设计
架构·agent
诚心呈意共享外卖配送系统3 小时前
怎么搭建同城聚合配送平台?一套接单、派单、算账的分层系统架构设计
架构·系统架构·创业创新
努力努力再努力wz3 小时前
【Lua入门系列】从动态类型到变量作用域:彻底理解 local、全局变量与跨文件访问
开发语言·junit·lua
国科安芯3 小时前
从点火到入轨的毫秒尺度:商业运载火箭飞行时序控制中的抗辐射MCU可靠性边界—— 面向短时高可靠任务的单粒子效应证据与架构分析
网络·人工智能·单片机·嵌入式硬件·架构·火箭·抗辐射
Dola_Zou3 小时前
从二进制加固到离线鉴权 工业边缘AI安全防御架构
人工智能·安全·ai·架构·软件加密