Skip to content

12306 系统抢票原理

12306 是全球规模最大的实时交易系统之一,春运期间日 PV 超 500 亿,日售票量超 2000 万张,峰值 QPS 达数十万。理解它的设计,是学习高并发系统架构的极佳素材。


整体架构

公私有云分流

系统面临的核心矛盾:查询量远大于交易量(查询占比超 75%),但交易的安全性要求更高。

子系统部署位置原因
查询系统(余票查询、车次查询等)公有云读多写少,弹性扩容方便,应对春运峰值
核心交易系统(购票、支付、退票等)私有云涉及资金与用户隐私,安全性要求高
用户系统私有云敏感数据,自主管控
  • 公有云可在春运期间快速扩容,平时缩容节约成本
  • 核心交易走私有云,保证数据安全与合规

微服务拆分

12306 将系统拆分为多个独立服务:

  • 用户服务、车次服务、余票服务、订单服务、支付服务、候补服务、退票服务等
  • 每个服务可独立扩缩容,互不影响
  • 服务间通过消息队列异步通信,降低耦合

余票计算:位图方案

问题:车票组合爆炸

一趟经停 N 站的列车,车票区间组合数为 N × (N-1) / 2。一趟北京→上海经停 5 站的列车就有 15 种区间票。全路网每天数千趟列车、上万种区间,传统数据库关系表的数据量和锁竞争都会爆炸。

位图方案

核心思想:用二进制位表示每一段是否有空座,位运算判断区间是否有余票。

以一辆 3 站列车(A→B→C)为例,座位被分成 2 段:

段1: A→B    段2: B→C

位图: [1] [1]   ← 每一位代表一段是否空闲(1=空闲,0=已售)

买 A→C 全程:段1 + 段2 相加,不溢出则有票
买 A→B 区间:段1 为 1,有票
买 B→C 区间:段2 为 1,有票

判断逻辑:将目标区间涉及的所有段的位相加,任何一段溢出(即该段位图中所有座位的该段都已售出),则无余票。

位图方案的优势

对比项传统数据库位图方案
数据量组合爆炸,N 站列车有 O(N²) 条记录只需 N-1 个段 × 座位数,数据量 O(N)
并发锁行级锁,高并发下锁等待严重位运算可做到原子操作,减少锁粒度
查询速度多表 JOIN,慢位运算,极快
内存占用紧凑,适合缓存

位图计算推测是原子性的——一个座位一个座位算,避免并发冲突。


全程优先与区间限售

问题:全程票有余,区间票售罄

同一趟列车,为什么全程票还有,但中间某段区间票却先卖完了?

如果中间热门段被短途旅客占满,全程旅客就无法买到票,导致列车末端座位空跑,运力浪费。

解决方案:全程优先,区间限售

  • 开售时优先给全程区间放票,短程票只放少量或不放
  • 随着时间推移逐步给中间区间放票
  • 越靠近发车时间,短程票越多,抢到票的概率越大
  • 一开始抢不到票不一定是票被抢完了,而是票放得少

考量因素

视角逻辑
铁路系统保障整体运力最大化——座位都坐满别空运,旅客周转量最大化
公平性长/短途旅客是零和博弈,应优先保障选择权较少的旅客需求
短途旅客短途有更多替代出行方式(大巴、自驾等),长途选择有限

放票时间窗口

  • 车票通常提前 15 天开售
  • 不同区间分时段放票,而非一次性全部放出
  • 系统会根据历史数据动态调整各区间放票比例

候补机制

问题:买不到票的焦虑

区间限售让大部分人无法第一时间抢到票,且不确定性造成强烈焦虑。

候补购票流程

用户提交候补 → 预付票款 → 进入候补队列 → 有票时按排队顺序自动分配 → 通知用户

                        退票池优先匹配候补 → 匹配成功 → 自动购票
  • 候补是先交钱排队,有票自动购买,无需反复刷新
  • 可设置截止候补时间,到期未成功自动退款
  • 属于帕累托改进——没人利益受损,候补者获益

候补的系统性价值

维度作用
信息收集系统通过候补数据感知哪里缺票、缺多少,从而决定是否加开列车
退票优先匹配退票先进入退票池匹配候补,而非流入公开市场——第三方爬虫抢不到
打击黄牛后续公开市场只剩候补消耗不完的票,黄牛可抢的票极少
优先级保障候补乘客优先级高于所有抢票脚本——脚本再快也在候补之后

候补机制之前,系统不知道缺票的精确分布,也无法科学决策是否加开列车。候补让供需信息变得透明。


高并发应对

缓存策略

  • 多级缓存:浏览器缓存 → CDN 缓存 → 本地缓存 → 分布式缓存 → 数据库
  • 余票数据读多写少,天然适合缓存
  • 购票时先扣缓存再异步写库,提高响应速度
  • 缓存与数据库的一致性通过消息队列保证

数据库分库分表

  • 按车次/日期分片,将流量分散到多个数据库实例
  • 订单表按用户 ID 哈希分表
  • 读写分离:查询走从库,交易走主库

限流与降级

  • 限流:接口级别的 QPS 限制,防止雪崩
  • 排队:购票请求先进入消息队列,异步处理,削峰填谷
  • 降级:极端情况下关闭非核心功能(如注销查询、广告位),保障核心购票链路
  • 验证码:防止机器人刷票,增加请求成本

异步处理

  • 购票请求 → 消息队列 → 异步处理 → 通知结果
  • 用户提交后无需同步等待,避免连接长时间占用
  • 候补、退票匹配等均为异步任务

防黄牛与公平性

多层防御体系

层级措施
前端验证码(滑块/点选)、请求频率限制、设备指纹识别
接入层IP 限流、异常流量识别、WAF 防护
业务层实名制认证、人证比对、同一身份限购
候补层退票优先候补、公开市场余票极少、脚本优先级最低
数据层请求去重、防重复提交、分布式锁

实名制 + 人证比对

  • 购票需实名,进站需人证比对
  • 同一身份证同一车次限购一张
  • 彻底杜绝了"屯票倒卖"的可能——黄牛买得到但用不了

验证码的演进

  • 早期:简单数字验证码 → 被 OCR 轻松破解
  • 中期:图片点选(找所有红绿灯等)→ 提高机器识别难度,但也误伤用户
  • 现在:行为验证(滑块、轨迹分析)→ 兼顾安全与体验

技术栈概览

层级技术选型
前端Vue + Nginx + CDN
网关Spring Cloud Gateway + 限流
服务Spring Cloud / Spring Boot 微服务
缓存Redis Cluster(余票缓存、分布式锁)
消息队列RocketMQ / Kafka(异步处理、削峰)
数据库MySQL 分库分表(ShardingSphere)
搜索Elasticsearch(车次查询)
监控Prometheus + Grafana

关键启示

  • 读多写少的系统,缓存是最好的武器
  • 组合爆炸问题,往往需要换一种数据结构(如位图)才能解决
  • 高并发不等于高性能——限流、排队、降级是比"更快"更重要的策略
  • 公平性设计本身就是技术问题——候补机制用信息透明替代了零和博弈
  • 系统架构没有银弹——公私有云混合、同步异步结合、缓存与数据库并存,都是权衡

参考资料

Move fast and break things