本文目录导读:

- 项目背景与业务痛点
- 系统总体架构设计
- 高并发秒杀核心设计(关键步骤)
- 核心数据库表设计(精简版)
- 防黄牛与风控体系
- 异常场景处理与容灾
- 性能指标测试参考
- 技术栈汇总
- 总结:该案例的亮点(面试展示重点)
这是一个经典的票务系统案例设计文档,我会以一个大型演唱会/体育赛事的票务系统为背景,从业务痛点、系统架构、核心流程、数据库设计、高并发解决方案以及防黄牛/防刷等维度进行拆解。
你可以直接参考这个案例来进行系统设计面试或作为项目原型。
项目背景与业务痛点
背景: 某头部主办方(如“周杰伦演唱会”或“NBA中国赛”)开票,瞬间涌入百万级用户抢购十万张门票。
核心痛点:
- 超卖: 数据库库存扣减错误,导致多卖票(无法交付)。
- 雪崩: 瞬间高并发打垮订单服务,导致系统宕机(一票难求)。
- 黄牛: 机器脚本刷票,导致真实用户抢不到票。
- 抢座冲突: 高并发下,同一个人座位被多人同时锁定。
系统总体架构设计
为了保证高可用、高并发,采用微服务 + 分布式缓存 + 异步削峰的架构。
[客户端]
(APP/H5/小程序)
|
| HTTPS + CDN
v
[负载均衡层] Nginx / 阿里云SLB
|
v
[接入网关层] Spring Cloud Gateway (限流/路由/风控)
|
|------------------------------|
| |
v v
[用户服务] [票务核心服务] ---- [风控服务/黑名单]
|
| (秒杀专用)
v
[Redis 集群]
(库存预热 + 分布式锁)
|
| 异步削峰 (MQ)
v
[RabbitMQ / RocketMQ]
|
[订单消费服务] (串行化落库)
|
v
[MySQL 主库]
(订单表 + 库存表 - 乐观锁)
高并发秒杀核心设计(关键步骤)
秒杀的难点在于“读多写少”,前端读(查询库存/座位)可以走CDN缓存,关键是扣减库存。
库存预热(提前放入Redis)
- 在开票前1小时,将门票的
stock字段载入Redis。 - Redis Key 设计:
ticket:stock:{sessionId}->100000(整数)。
原子扣减库存(Lua脚本防并发)
- 使用Redis的Lua脚本来保证“检查库存”和“扣减库存”的原子性,防止超卖。
- 核心逻辑(伪代码):
-- 若库存大于0,则减1 local stock = redis.call('GET', KEYS[1]) if tonumber(stock) <= 0 then return -1 -- 卖完了 end redis.call('DECR', KEYS[1]) return 1 -- 扣减成功
限流与防刷(网关层)
- 单用户限流: 同一个UID,1秒内仅允许1次抢购请求进入后端。
- 设备指纹: 检测异常高频请求的IP或设备ID,直接返回“请求过于频繁”。
异步下单(削峰填谷)
- 扣减Redis库存成功后,不直接写MySQL(不然数据库瞬间压力爆炸),而是发送一条
MQ消息。 - 后端订单消费者(消费速度恒定)从MQ拉取消息,进行MySQL的最终扣减与订单生成(状态:待支付)。
- 注: 这里的MySQL扣减也必须使用乐观锁
UPDATE stock SET version = version + 1 WHERE id = ? AND version = ?作为最终兜底。
用户态轮询
- 抢购成功后,前端不刷新页面等待,而是轮询订单服务接口,查询订单是否已生成,若生成跳转支付页。
核心数据库表设计(精简版)
票档表(ticket_sku)
| 字段名 | 类型 | 说明 |
|---|---|---|
id |
bigint | 主键(票档ID,如:内场A区) |
session_id |
bigint | 场次ID |
name |
varchar | 票档名称(看台/内场) |
price |
decimal | 售价 |
total_stock |
int | 总库存 |
version |
int | 乐观锁版本号(防超卖兜底) |
订单表(orders)
| 字段名 | 类型 | 说明 |
|---|---|---|
id |
bigint | 主键 |
order_no |
varchar | 全局唯一订单号(雪花算法) |
user_id |
bigint | 用户ID |
sku_id |
bigint | 票档ID |
status |
tinyint | 0待支付 / 1已支付 / 2已取消 |
create_time |
datetime | 下单时间 |
座位锁定表(seat_lock)(如为选座系统)
| 字段名 | 类型 | 说明 |
|---|---|---|
id |
bigint | 主键 |
session_id |
bigint | 场次ID |
row_no |
int | 排号 |
seat_no |
int | 座号 |
user_id |
bigint | 锁定用户 |
status |
tinyint | 0空闲 / 1锁定 / 2已售 |
lock_time |
datetime | 锁定时间(超过10分钟未支付自动释放) |
防黄牛与风控体系
- 强实名制: 下单时绑定身份证,入场时人脸识别比对。
- 风控规则引擎:
- 同一收货地址下单超过N次 -> 触发拦截。
- 同一IP段/设备ID高频请求 -> 加入黑名单返回错误码。
- 支付账号与实名信息不一致 -> 拦截。
- 行为验证: 在提交订单前加入滑块验证(如极验)或点选验证,增加机器脚本的破解成本。
- 异步检测: 对于“秒杀成功”的账号,后台异步分析其历史行为(如:是否刚注册、是否有退票记录),若判定为异常,则强制取消订单(熔断机制)。
异常场景处理与容灾
-
用户重复点击(幂等性):
- 前端: 点击抢票后按钮置灰。
- 后端: 使用
userId + skuId生成幂等Key,存入Redis(SETNX),若已存在则不重复生成订单。
-
库存超卖兜底:
- 第一层: Redis Lua原子减。
- 第二层: MySQL
UPDATE ... WHERE total_stock > 0(行锁),即便Redis挂掉,数据库也会挡住超卖。
-
支付超时:
- 使用延迟队列(RocketMQ延迟消息)或定时任务(xxl-job)扫描“待支付”订单。
- 若超时15分钟未支付,则将Redis中的库存回滚+1,并释放座位锁定,供其他用户抢购。
性能指标测试参考
- QPS 压测目标: 核心抢票接口支撑 10万+ QPS(通过Redis支撑)。
- 下单延迟: 30秒内完成订单异步落库(保证不阻塞抢购主流程)。
- 成功率: 系统可用性达到 99%(避免因流量洪峰导致雪崩)。
- 端到端延迟: 用户点击抢票,在 2 - 3 秒内响应“抢票成功/失败”提示。
技术栈汇总
| 组件 | 技术选型 | 用途 |
|---|---|---|
| 负载均衡 | Nginx | 接入层入口,动静分离 |
| 微服务框架 | Spring Boot + Spring Cloud Alibaba | 业务逻辑实现 |
| 缓存中间件 | Redis (Lua脚本) | 库存扣减,分布式锁 |
| 消息队列 | RocketMQ / RabbitMQ | 异步下单、流量削峰 |
| 数据库 | MySQL (InnoDB) + MyBatis-Plus | 最终库存和订单持久化 |
| 分布式追踪 | Zipkin / SkyWalking | 排查高并发下的全链路瓶颈 |
| 监控告警 | Prometheus + Grafana | 监控业务QPS、服务资源、Redis内存 |
该案例的亮点(面试展示重点)
回答思路: “在构建该系统时,我主要关注了数据一致性和高可用两点,针对高并发秒杀场景,我没有采用传统的数据库行级锁(会导致阻塞耗时),而是采用了 ‘Redis预扣减 + MQ异步落库 + 数据库乐观锁兜底’ 的三级方案,通过Lua脚本保证原子性,成功将数据库的写压力从百万级瞬间并发,降级为平稳的异步消费,在业务上利用IP限流和幂等性设计,有效解决了超卖和黄牛刷单的痛点。”
如果你需要,我可以进一步为你细化某一个模块的设计,
- 如果是“高铁/电影”这种“锁座选座”的场景,怎么处理锁冲突和死锁?
- 如何设计一个通用的“延迟消息队列”来处理未支付订单的回滚?
告诉我你的侧重点,我可以继续展开。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播