前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >2024年春运抢票大作战:揭秘12306,为什么你的票总是“飞”了?

2024年春运抢票大作战:揭秘12306,为什么你的票总是“飞”了?

作者头像
xiaoyi
发布2024-04-10 16:02:27
1230
发布2024-04-10 16:02:27
举报
文章被收录于专栏:小义思小义思

唯一的车票软件

大家好,我是小义。2024年除夕才能放假,相信这几天很多人都在抢回家的票,网上很多的抢票软件其实最后都是走的12306,没有其他便捷途径,只不过是可以预约自动抢票。可能很多人认为12306软件只不过是一个卖车票的简单平台,不需要很高的技术含量,但其实说它是全国最庞大且最繁杂的系统也不为过。12306的业务复杂度远远比淘宝京东这种电商网站要复杂得多。

现在回想一下自己买票的经历,是不是有时候中途票很难抢,终点站却又有票;是不是不同目的地的票预售时间还不一样;是不是有时候看到有余票,但是点进去又说没票了。如果你也有这些疑问,那就和小义一起,从技术与业务的角度出发,分析一下12306到底是怎么卖票的。

12306等于电商平台?

我们平时接触最多的就是电商平台,12306也确实是一个电商系统,车票就是商品。但是车票这个商品不是像衣服那样,卖掉一件库存就减一这么简单,因为车票中途可能有很多个站点,每个站点都可以买票上下车。

以北京西到深圳北的G81车次高铁为例,列车依次经过:北京西->石家庄->鹤壁东->郑州东->驻马店西->武汉->长沙南->衡阳东->广州南->深圳北,共有10个站,3种座位(商务、一等、二等,暂不考虑无座情况)。那么G81车次共有多少件商品呢,答案是45 * 3 = 135,可以理解为135个SKU。数字3很好理解,三种座位,那45怎么来的呢?其实从北京西出发,可以买从石家庄,到深圳北中的任意一个站点的票,共9种,都是G81这个车次,该线路上的其他站点同理可得8、7...2、1,因此这是一个组合问题,C102=(10 * 9)/(2 * 1)=9+8+7+6+5+4+3+2+1=45。

一趟车次能卖多少张票

既然G81车次有这么多种商品,那它能卖多少张票呢?我们假设只有线上渠道卖,并且G81一共就100个座位。那么从北京西到深圳北也就是从起点坐到终点的票最多可以卖100张,但是如果有一个人买了北京西到石家庄的,那全程票只能卖99了,所以这会涉及到动态库存的变化。相邻两个站点之间,例如北京西到石家庄的票,广州南到深圳北的票,其实最多也是只能卖100张票(座位就那么多)。如果G81列车车票都是按两个相邻站点的票卖,那么整条线路一共有9段,可以卖出100*9=900张。终上所述,G81卖出的票肯定在[100,900]这个区间。

现实中车票不可能都是网上卖,线下售票厅也会存一部分,而且铁路运营在卖出更多票的同时,一般会优先满足从始发站乘车到终点站的长期旅客的需求,因为这些旅客通常具有刚性的旅行需求和较小的选择范围。当然,这样子做也是为了保障长途列车的运力得到充分利用,总不能让列车拉空车厢吧。所以像G81这个车次肯定是从起点一直坐到终点的票是卖最多的,另外像郑州、武汉这种上的人多的地方也会预留更多的票。1000张票,系统可能控制全程票卖700张,郑州、武汉站各卖100张,其他中间站100张。所以现在明白为什么抢中途票总是抢不到,但是终点却又有票了吧。

库存模型设计

为了方便分析系统售票的库存模型,我们从简单入手,先定义一列G1车次依次经过ABCD四个站点,同时忽略座位类型,那该车次就只有6种商品,分别是AB、BC、CD、AC、AD、BD共6个不同区段的票。用线段来表示整条线路,AB、BC、CD是三个独立的线段,也叫做原子区间,其他区段的票都会至少和其中的一条相关联。线段与线段之间会相互覆盖,其实就是存在座位竞争,买票的时候肯定是要避开这种情况的,不然乘客不得打起来了。

用数字0和1来代替区段的话会更加的清晰明了,0为原子区间不被占用,1则占用。对于这趟列车来说三个数字长度就够用了,从左往右依次指代AB、BC、CD,那么数字100就表示卖的是AB的票,110表示的是AC。最后再忽略掉不同区间的票数分配比例,总票数就8张,也就是车上只有8个座位,那么就可以得出下面的车票库存表。

id

日期

车次

区间起点

区间终点

区间标识

余票

1

2024-01-01

G1

A

B

100

8

2

2024-01-01

G1

B

C

010

8

3

2024-01-01

G1?

C

D

001

8

4

2024-01-01

G1

A

C

110

8

5

2024-01-01

G1

A

D

111

8

6

2024-01-01

G1

B

D

011

8

光看表中的区间标识字段,是不是很熟悉,这不就是二进制嘛。那么库存表有了,售票的时候怎么计算呢?如果现在有个人小a,买了CD的票,那和CD有关的库存类型都要减一,也就是id等于3,5,6的行,余票都要减一。其实只要将CD的区间标识与分别与其他区段的区间标识两两与运算,只要不为0那就是与CD有关的区段,库存都需要修改。按这样的库存计算方式,只要有余票,那就都可以售票。

如何选座位

选好了票,接下来怎么选座位呢?同理可以用0和1的比特位来表示。

如果说刚才的小a是第一个买票的人,那可以给他分配位置00000001,然后存入用户记录表中。如果再来个小b买BC的票,因为和小a不存在座位冲突,同样可以给小b分配第一个座位。但是这时候小c来买AD全程票,会发现没办法分配第一个座位,只好按顺序给他分配第二个座位。

id

用户名

日期

车次

区间标识

座位标识

1

小a

2024-01-01

G1

001

00000001

2

小b

2024-01-01

G1

010

00000001

3

小c

2024-01-01

G1

111

00000010

其实这里举的例子用到的是回收利用优先的座位选择方法。当给用户选座位时肯定是先看之前的乘客都选了哪些座位,但是也不用将所有乘客都筛出来看一遍,只要筛区间冲突的就好了。

当小b选位置时,将记录表中凡是区间标识字段值第二个数字为1的记录行都选出来,然后做一个总的座位标识的与运算,恰好小b选的时候一条冲突的都没有,结果就是00000000,八个都可以坐,因为这个时候小a其实已经下车了,第一个座位被回收利用了,就优先分配给小b了。等到小c选的时候情况就不一样了,因为小c是全程票,得拉出所有乘客的数据看,所以小a和小b都和他有冲突,但是只冲突了一个位置, 所以就给他分配了第二个座位。

数据存储与计算

可以看到,虽然只是一个经过ABCD四个站点的小车次,但计算已经不简单了。像北京西到深圳北G81这趟列车,如果用穷举法列出所有商品,并用Mysql数据库存储数据的话,那得多复杂,大佬们可以自己算算。而且当涉及到数据库表多条记录行计算的时候,显然是一个大事务,一不小心就会造成阻塞,再加上大流量下的频繁操作,一不小心数据库就会崩溃,这对于系统来说都是万万不能承受的。以上只是对12306库存模型的简单分析,12306也不可能直接用数据库来实现这么复杂的业务逻辑。

可能很多人会说用redis的bitmap位图结构来处理比特位的运算与存储,的确是个好方法,而且在内存中计算也会比数据库快很多。12306也确实用到了内存数据库,但是他们的内存从几T到十几个T,出乎意料的大,用的也是Pivotal Gemfire这种高大上的内存数据库。至于gemfire和redis有啥区分,感兴趣的小伙伴可以自行研究。

继续优化模型

截至目前12306也没有公开公布技术方案,以上讨论的库存模型也只是小义查了相关资料之后得出的猜想,那么刚才的库存模型有没有优化的点呢?

还是G1这趟车,ABCD四个站点,其实可以只存三种库存,分别对应三个原子区间。

id

日期

车次

区间起点

区间终点

区间标识

余票

1

2024-01-01

G1

A

B

100

8

2

2024-01-01

G1

B

C

010

8

3

2024-01-01

G1?

C

D

001

8

不管是买个什么区段的票,该区段内对应的所有原子区间库存都要减一,只要能减成功,那就可以出票,退票的话就是区段内对应的所有原子区间余票加一。

像刚才小a买了CD票,小b买了BC票,小c买了AD票,那库存表最终结果如下:

id

日期

车次

区间起点

区间终点

区间标识

余票

1

2024-01-01

G1

A

B

100

7

2

2024-01-01

G1

B

C

010

6

3

2024-01-01

G1?

C

D

001

6

这样子计算量就比前面的小很多,当然选座位还是一样方式。不过不知大家有没有发现,优先回收利用座位选择法每次选位置选到的都是一个固定位置,中途不会换座位,但是每次都要筛出冲突的乘客然后进行座位比较,可能会比较低效。

其实也可以按优先分配空闲座位的方式来分配,还是刚才的例子,如果说G1这辆车只有2个位置,a买CD票分配了座位1,b买BC票分配了空闲座位2,那当c买AD票时,已经没有空闲的了,只能比较座位了,但计算方式会有点麻烦,筛出所有冲突的票之后得先看每个原子区间是否有空闲的座位号,然后再考虑座位的连贯性,所以c在AC段坐在座位1上,等到CD段的时候换回座位2了。

id

用户名

日期

车次

区间标识

座位标识

1

小a

2024-01-01

G1

001

01

2

小b

2024-01-01

G1

010

10

3

小c

2024-01-01

G1

001

10

4

小c

2024-01-01

G1

110

01

总结

以上只是小义对12306系统库存设计的一些思考,剔除了座位类型,售票渠道,站点票数分配比例等各种因素之后,其实库存模型还是没有想象中的那么简单。所以12306确实厉害,以上讨论的内容,也只不过是它的冰山一角,欢迎大家批评指正,也可以关注小义公众号获取联系方式,和小义深入交流。

最后,希望大家都能买到票,安全回家,开心过年!

本文参与?腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2024-01-24,如有侵权请联系?cloudcommunity@tencent.com 删除

本文分享自 程序员小义 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与?腾讯云自媒体分享计划? ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 唯一的车票软件
  • 12306等于电商平台?
  • 一趟车次能卖多少张票
  • 库存模型设计
  • 如何选座位
  • 数据存储与计算
  • 继续优化模型
  • 总结
相关产品与服务
数据库
云数据库为企业提供了完善的关系型数据库、非关系型数据库、分析型数据库和数据库生态工具。您可以通过产品选择和组合搭建,轻松实现高可靠、高可用性、高性能等数据库需求。云数据库服务也可大幅减少您的运维工作量,更专注于业务发展,让企业一站式享受数据上云及分布式架构的技术红利!
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
http://www.vxiaotou.com