原创:谈谈12306铁路客运售票系统的架构问题(四、最终篇)

作者:刘常军(2014-01-20)

经过前面三篇文章的铺垫和说明,如今终于可以具体分析要如何对12306.cn的架构进行优化了。
在这里我要强调,我不是该系统的架构师,没看过12306.cn的设计说明书,跟该系统的开发单位也无业务往来,对12306.cn铁路客运售票系统的架构问题,仅是本人出于对IT技术进步的追求而自发的进行思考和分析,进而形成的一些技术上的分析意见,如有雷同实属巧合。
首先我们来简要分析一下12306.cn网站架构为什么是现在这个样子,而不是淘宝、京东那样的架构,也不是道路客运售票系统那样“天然的”分布式系统架构?这不是巧合,而是一系列的因素形成的合力所导致。(1)受铁路系统行政管理体制的影响,12306.cn必须是一种集中式系统架构。(2)系统的承建单位是铁道部信息技术中心、中国铁道科学研究院,不是淘宝、京东这类的互联网企业。(3)12306.cn要满足其核心业务需求。即12306.cn出售的商品(即火车票)是动态的,要随时根据乘客已购车票的起点站和终点站进行拆分与合并操作,这也是12306.cn与淘宝、京东这类互联网购物平台的最大区别。
现在我们来谈谈系统的优化问题,其实可以将12306.cn的优化分两大块来谈。
(一)关于旅客、订单及常用 联系人。
这部分数据架构比较简单,以下是未拆分的数据ER图:
从图上我们可以看出,主表数据是“旅客”,而“订单”和“常用联系人”是“旅客”的字表数据,一对多的关系,即一名旅客可以有多份订单和多个常用联系人。从主表数据来看,由于任意两个旅客之间不存在关联关系,对一个旅客数据进行任何操作,不影响另一个旅客数据,因此这部分数据结构完全可以采取“拆分+缓存”的策略进行优化。
“拆分”技术主要是进行水平拆分。水平拆分的规则不复杂,方法也很多,例如可以很简单的对“旅客ID”进行哈希运算,进而均匀分布到若干个水平分区表中,然后根据每台x86服务器的硬件资源配置部署一定数量的水平分区表,最终达到通过若干台x86服务器实现这部分数据拆分的目标。
“缓存”技术主要是应对高并发的。数据水平拆分后,减少了每台服务器上的数据量,使得数据不那么集中。但是面对春运期间汹涌而来的高并发,有必要进一步采取缓存机制加以应对。从数据架构上我们看到,这部分数据的主要操作其实是读(SELECT)操作,而插入(INSERT)和更新(UPDATE)操作相对来说比较少,所以可以采取缓存技术,将数据库中的信息进行缓存,当客户端传来读操作请求时,将其重定向到缓存中去执行读操作。当数据库进行了插入(INSERT)和更新(UPDATE)操作后,由于后台去主动刷新缓存,以此提高系统的读操作性能。
(二)关于车次、站点、席位及车票。
这部分数据架构不甚复杂,但数据间的操作相当复杂,可以说整个12306.cn的核心运算都在这里,也是12306.cn与其他电子商务平台的最大区别。正是因为这里运算复杂,所以才会导致其数据拆分很难实现,进而在高并发时几近瘫痪。我们先看看它的ER图:

 
从这里我们可以看到,车次、站点、席位类别三个表的数据是可以预先生成的。其中,“车次”是用来存储“G72广州西-北京南”、“D5北京-沈阳北”这样的完整线路信息的。“席位类别”用于存储该车次上各种类别的席位,例如“一等座 5车厢08号”、“二等座10车厢15号”等此类信息。“站点”用于存储该车次从始发站开始,至终点站结束,期间所经停的所有途经站点,包括站序、站点、到达时间、出发时间等一系列信息。这三个信息相对来说是静态的,只要车次确定,那么这三个信息就是确定的,因此可以在开车前全部预先生成好。
但图里用黄颜色标记的“车票”表,里面的数据则是动态生成的,核心算法全在这里。为什么这里要动态生成?这个问题在前面的《谈谈12306铁路客运售票系统的架构问题(二)》文章里已经做过说明,这里再复述一遍:
假设有A旅客买了一张“广州-长沙”的票(一端为起点),那么票务库应当立刻取一张“广州-北京”的票将其一拆为二,一张是“广州-长沙”,卖给A旅客,另一张是“长沙-北京”,保存到车票库里。假设有B旅客买了一张“武汉-郑州”的票(中间段),那么票务库应当立刻取一张“广州-北京”的票将其一拆为三,一张是“武汉-郑州”,卖给B旅客,其余两张“广州-武汉”及“郑州-北京”的票则保存到车票库里。实际在拆分时还会涉及到很多优先级算法,例如旅客要买短途票,那么系统是优先拆长途车票、还是优先拆短途车票?旅客要买长途票,但车票库里已经没有符合条件的长途票,只有一些很零碎的车票了,那么能不能把几张很零碎的车票拼起来卖给旅客?……诸如此类,要考虑很多细致的算法,这样才能保证将尽可能多的车票卖出去,降低列车座位的空座率,减少浪费。
从上面这段需求描述中,我们可以看到,在12306.cn里,“车票”数据是核心数据,跟淘宝、京东这样的电子商务平台上的“在售商品”数据所处地位类似。但不同的是,“车票”表内的数据,其中某一条数据的产生严重依赖表中另外一条数据,新数据的插入(INSERT)通常是伴随着老数据的更新(UPDATE),并且还涉及到要对多条老数据进行优先级判断,正是因为这种复杂的算法导致了车票表的数据很难被拆分。之所以出现了这么复杂的算法,应该是12306.cn系统必须要满足几个基本需求——即先卖有座的票,有座票卖完了再卖无座票;购买短途票需要把长途票拆开,剩下的旅程的座位票要接着卖,尽量让座位在12306上卖出去,不能让中途上车补票的人有座位。基于这样的需求,我估计,12306的系统架构师在设计的时候,自然而然的将“票源”和“席位”混合在一起进行存储和计算了。于是导致原本是两个独立的简单对象,但如果把它们当作一个对象来分析,那计算逻辑就复杂多了,牵一发动全身!所以12306.cn前段时间的性能调优工作才没有去改动架构和算法,原来怎么算的、现在还是怎么算,只不过现在是把这种计算全部搬到内存里去,借助内存的高速计算能力达到性能提升的效果。
综上所述,关于12306铁路售票系统的架构问题,首先我建议首先在系统架构层面将“票源”和“席位”两个对象独立开、分别进行考虑。改进后的系统架构ER图如下:
  在改进后的架构里,票源就是单纯的票源,不跟席位挂钩,票源就是标记本次旅程的起点站和终到站。这样,既然某条线路的始发站、所有途经站和终点站全部已知,那么把这些站点两两组合,通过穷举法就可以预先生成全部的票源信息。席位就是单纯的席位,通过车次、车厢及席位号三个指标就可以唯一确定一个席位,一趟列车有多少个席位是确定的,因此席位信息也可以全部预先生成。这里注意席位信息中的“席位占用”字段,要根据本车次全程站点数生成一个对应位数的字符串(或者二进制数值也可以),例如某次列车从始发站到终点站有20站,那么就先生成一个20位的字符串(或者二进制数字)。这个字符串从左到右每一位字符分别用1和0标识该席位是否已被占用,最初生成的时候当然全都是0。
其次,乘客在购票时,后台系统改为这样的计算方法:(1)根据旅客选择的车次、起点站、终到站,查询出票源。由于票源不跟席位关联,并且已经全部预先静态化,因此这一步肯定会查到唯一一条票源信息。(2)系统根据旅客选定的起点站和终到站,生成一个席位申请标识,例如某次列出从始发站到终点站有20站,旅客本次购买的车票是从第3站上车到第8站下车,那么这个标识就是一个20位的字符串(或者二进制数),其中第1-2位是0,第3-8位是1,第9-20位是0。(3)用前一步生成好的席位申请标识到“席位”表中去跟“席位占用”字段进行匹配,匹配出来之后,立即更新该席位记录的“席位占用”字段,同时取得席位ID。(4)用前面获取到的票源信息和席位信息,再加上旅客信息,最终生成“订单”信息。
上面这个算法的关键之处有两点,一是通过增加票源对象,把票源和席位独立开来,通过“空间换时间”,提前把一个车次可能会用到的数据全部静态化。二是把原来的车票数据的动态增删改操作转换为一个字段的简单更新,降低了操作维度,减少了数据更新的复杂性。通过这种方式进行改进后,12306.cn的核心数据不再是互相关联的数据,不会出现售票时的动态生成操作。至此,经过此番系统架构的调整,核心数据也可以采取“拆分+缓存”技术来应对大数据量和高并发了。
后记(一):在写作本文接近尾声之际,我看到了这篇文章《对话12306技术负责人:春运能否不瘫痪》(http://tech.163.com/14/0120/09/9J18KPPH000915BF.html),里面的12306技术负责人朱建生说了如下一番话:
淘宝是很优秀的网站,也是我们学习的榜样。不过,网售火车票与淘宝这种综合购物确实有很大不同。举个简单的例子,在淘宝出售一件商品,同样的库存只需减数量就可以了。但是车票不一样,它具有不可替代性。
前一段网上有个帖子,例子举得挺对。北京西到深圳北的高铁,它有17个站,3种座位。表面看起来,就是3个产品,即商务座、一等座、二等座。但实际上,它有408种商品,因为旅客的上车站和下车站都可以是不同的选择,每一张票都是一个相对独立的商品,而且这种产品是动态的。如果售出了一张北京到武汉的二等座,客票核心系统会立即自动生成一张武汉到深圳北之间的同席位二等座,同时取消北京至石家庄直至武汉之间车站的车票,此外,还需将这些车票数量的变化实时同步到网站上。因此售票系统是在不停地计算生成新的“商品”。
这段话完全证实了我对于12306.cn架构的猜想。因此,希望本文能对12306.cn的改进有所帮助,希望明年我们买票能是买车票,而不是买彩票。
后记(二):有朋友问我,为什么能想到这个算法?原因很简单,因为我想起了我小时候在家乡坐“小火车”的情形。我小时候在东北林区长大,那里有一种所谓的森林铁路,其轨道比通常的火车道要窄一些,火车头和车厢也都小一号。这种铁路是专门用于从山上运送木材到镇上的,客运只是其一小块业务。每次坐这种“小火车”时,买票完全无压力,开车前随时都可以买到。但是买到票只是允许你上车,上车以后要想有座,必须有“座号”!”座号“就是大概一平方厘米那么大的一张薄薄的小纸片,上面印着车次、车厢和座位号。有它就有座,没它就站着。“票号”不收费,但它是很紧缺的资源,除非你早点去买票或者认识车站的人,否则就站一路吧。后来到90年代前后,国家保护森林资源的力度逐渐加大,树木都不让砍了,“小火车”也无声无息的消失在历史的长河中。如今镇上和各个村子之间的运输任务,全部由客车、货车来完成。
后记(三):坚守了几个晚上,作者我终于抢到一张半夜零点左右到家的动车票,时间早晚问题咱就不挑了,这年头能有张票就不错了,还要啥自行车!

转载于:https://www.cnblogs.com/liu7537/p/3528832.html

原创:谈谈12306铁路客运售票系统的架构问题(四、最终篇)相关推荐

  1. 原创:谈谈12306铁路客运售票系统的架构问题(二)

    原创:谈谈12306铁路客运售票系统的架构问题(二) 作者:刘常军(2014-01-10) 就一个软件系统而言,业务需求是根本,是软件系统的基因和灵魂. 下面我们来简要的分析一下铁路客运售票系统和道路 ...

  2. 在线订票是哪种计算机应用,铁路联网售票系统按计算机应用的分类它属于

    铁路联网售票系统按计算机应用的分类它属于信息处理.计算机的主要应用领域分为科学计算.信息处理.过程控制.网络通信.人工智能.多媒体.计算机辅助设计和辅助制造.嵌入式系统等,"铁路联网售票系统 ...

  3. python汽车票票系统_长途客运售票系统

    s" ); for(i=0;i&ltn;i++) /*冒泡法排序*/ { for(j=i+1;j&ltn;j++) if(strcmp(ti[i].data,ti[j].da ...

  4. 12306铁路订票系统的一个小bug

    进入12306的后台>>打开[我的信息]>>打开[常用联系人] 列表中上面是自己不能修改,下面是帮别人买票时自动添加上的联系人点击[编辑] 进入到的页面可以修改用户信息,生日默 ...

  5. 计算机售票作业6字法,铁路客运计算机售票具体操作(28页)-原创力文档

    铁路客运计算机售票具体操作 售票员接班后,先开 UPS 电源.制票机,再开显示器和 主机.在制票机初始化完毕后输入工号和密码 (口令),并仔 细核对票卷当前票号和计算机屏幕显示票号是否一致.确认 输入 ...

  6. 铁路售票系统_铁路资讯:复兴号动车、智能京张高铁…中国最高端铁路装备看这里...

    今天上午,两年一度的中国国际现代化铁路技术装备展在京开展,会期3天,将集中展示路网建设.客货运输.经营管理.工程建造.技术装备.旅客服务等铁路行业各领域的先进产品及技术. 展会现场 智能京张:将首次实 ...

  7. 售票计算机 制票机的使用方法,铁路客运计算机售票具体操作.pdf

    铁路客运计算机售票具体操作 售票员接班后,先开 UPS电源.制票机,再开显示器和 主机.在制票机初始化完毕后输入工号和密码 ( 口令 ) ,并仔 细核对票卷当前票号和计算机屏幕显示票号是否一致.确认 ...

  8. 铁路网络售票是利用计算机,铁路客运计算机售票具体操作图文.pdf

    铁路客运计算机售票具体操作 铁路客运计算机售票具体操作 售票员接班后,先开 UPS 电源.制票机,再开显示器和 售票员接班后,先开 UPS 电源.制票机,再开显示器和 主机.在制票机初始化完毕后输入工 ...

  9. 12306铁路售票系统核心开源中间件Geode介绍

    https://github.com/project-geode/docs Geode是一个提供实时且高一致性的分布式数据管理平台,典型案例是中国铁路12306售票系统使用Geode管理10个集群节点 ...

最新文章

  1. python面向对象(3)
  2. hadoop将消亡_数据科学家:适应还是消亡!
  3. 最高分数的学生姓名(信息学奥赛一本通-T1147)
  4. 【汇总】C#数据类型及转换
  5. Android View框架总结(九)KeyEvent事件分发机制
  6. 《大前端进阶 安全》系列 HTTPS详解(通俗易懂)
  7. 前端学习白嫖-QQ音乐
  8. Si9000阻抗计算笔记(一)
  9. matlab简支梁有限元分析,1.3 简支梁的有限元分析
  10. 浪潮java面经总结
  11. 阿里云 root ssh远程登录 及 普通非root用户 ssh远程登录 Ubuntu1604
  12. 熊孩子说“你没看过奥特曼”,赶紧用Python学习一下,没想到
  13. BZOJ 4826 HNOI2017 影魔
  14. 淘宝旺旺号转userid 和 uid 的接口方法
  15. 阿里云服务器的ECS和RDS和OSS和SLB是什么意思?
  16. Redis典型应用场景实战之抢红包系统
  17. cordova wifi插件(cordova plugin add cordova-plugin-hotspot)
  18. 基于javaweb+mysql的医药信息管理系统(java+SSM+HTML+easyui+mysql)
  19. raise ImportError(“html5lib not found, please install it“) ImportError: html5lib not found
  20. 奇偶数列(输出起始奇数最小的数列的最小奇数和数列长度)

热门文章

  1. 【备战十四届蓝桥杯 | 开篇】如何高效备战蓝桥杯
  2. 三级网络技术考过指南
  3. [激光原理与应用-62]:激光器 - 光学 - 怎样测量光斑和光束质量?
  4. Jackson - 将 JSON 字符串转换为 Map
  5. 补充说一下120亿光年
  6. 四大会计师事务所python数据分析_用Python玩转数据分析4
  7. 安装DirectX2010报错s1023,且找不到microsoft visual c++2010 redistributable文件
  8. 软件测试的目的是什么?(详谈)
  9. 【学会Matlab走遍天下】如何画正弦余弦曲线和(学习笔记)
  10. 总结sizeof的用法(包括位段)