首先有几个问题先说一下:
1 今年春节期间铁路客流量据说有31亿
2 目前12306 pv是14亿,而高峰期就在8点到10点,那么也就是有可能在这两个小时里有5亿访问量,而每秒的并发量估计在最高峰时能达到几千万
3 目前Ngix能处理在线1万,但是实际值一般是8000左右
4 一台IBM大型机要几千万美元,估计加上DB2,交易中间件,得小1亿了
5 腾讯,淘宝等拥有总在线人数4亿规模或者事务处理达到亿级别的规模耗时七八年,总投资估计上百亿 (腾讯资料:1亿在线背后的技术挑战)

6 绝对不能两个人订到同一张票,而看到有票,而点击了下订单又说没票了这种失误是可以容忍的。

 

假设一个车次能坐1000人,有10万个人想订这车次的这个时间的票,那么意味着这10万人要被分别负载到10台机器上,这10台机器要共同去维护这 1000张票的余量。简单的分库分表大家都会,但是可能是有成千上万台机器来处理这些用户,而且这些用户还可以订全国的火车票。

 

 

我想到了一些机制来处理,或者说先简化再处理问题:

总原则:不需要所有的人

1 职责分离,将登录,订票(查询,填表,下定单),支付,查订单,查车票等都分成不同的服务,采用不同的集群去处理。

2 登录系统,只要有足够的服务器,大家都可以登录进来,这个十分简单

3 订票,总原则是 让能够进入订票流程的人,快速无障碍的进入系统,设定15分钟为一个阀值,15分钟内成功下订单,则将其踢出订票系统,用户可以拿着订单号通过电话或者登 录支付系统进行支付(Apple的网上商店即这样),超过15分钟没搞定的也被踢出,提示操作超时。点击订票之后,进入前置分析机,分析机负责计算背后的 机器能负载多少用户下订单。比如目前有1百万人同时点击了订票,而背后只能负载10万人,那么出现一个随机摇号程序,摇出10万人,其他人返回 “系统繁忙,稍后重试”的提示。这10万人被负载在10台机器上,可以进行查询,当点击指定车票(标记为ClickSelectedTicket)后,根据车票被分散到不同的机器上(其实是MapReduce的思想)。比如有1万人被定位到要订票T1,系统扔出900张T1票,留100张容错(随着系统逐步稳定,可减少容错票数),然后大家抢锁,采用乐观离线锁。在最终提交订单时检测

4 可以采用地铁高峰限流的方式(其实Apple购买iphone时也类似),就是增加到达ClickSelectedTicket之后的页面的路径,可以多绕几圈,最终减少并发可能性。

5 采用token机制保障页面必须从第一步点击订票开始进入,不可以绕过中间步骤。以免刷票机器人对系统造成冲击(当然还要做IP限定)

6 将票分到几台服务器上,将购买到该车次该时间车票则将×××+车次+座位+时间作为key,这样验证是否此人已经订过该票一步搞定,然后异步统计余票。某台机器的票没有了,这台机器就被移除(类似于负载均衡原理,票没了就相当于机器挂了,目前常用的技术是心跳,还是异步统计)

 

 

还需考虑的问题是:一个座位分段卖出问题。各个铁路局分布式提供车票的问题。

 

自从2012开始那天,网络购买火车票成了国内最火的话题,12306.cn的Alexa排名从三个月前的全球万位以外迅速窜升至今日的全球排名1560位、中国排名102位。并且成为第11大电商网站。但是由于铁道部公开的种种原因,12306也让人诟病不止。

其中最大的原因就是登陆12306慢,页面打不开,好不容易打开了,无法查询票额,无法购买票,甚至只收钱不吐票:),在放票时间更是非常突出
很多网友及专业人士都给出了自己的见解,一下是一些建议截图:


 

  这些都是业内的专业人士根据其多年经验给出的自己建议。本人虽没有他们那么高深的学问和经验,但是因为也要购买火车票的原因及稍有些经验,所以也考虑了些架构方面的问题,希望和大家分享并讨论

 

    在水木社区看到一些人谈内存数据库可以怎样提高多大速度的话题,自己不太支持,首先12306并不像普通的互联网网站,可以允许一些差错,只要误差在合 理范围内,不会影响很大。但是像银行及12306这种涉及用户钱或者珍贵的火车票,我想很难容忍因为内存数据库崩溃导致的一系列问题。

 

一 静态资源的压缩优化及CDN分发策略

12306上涉及的图片及js、css等静态资源应进行压缩后传输,设置expires属性,在浏览器端缓存,减少对静态资源访问,提高页面访问速度。同时高效使用CDN分发策略,像北上广等一线城市应尽量分流,减轻服务器压力,防止服务器因压力过大宕机或IO低效 崩溃。

 

二  缓存车次信息及余票

 用户登上网站后,除了登录操作流程外最火的两个操作流程应该是购票和余票查询。余票查询应及时给用户返回查询信息。所以需要优化缓存结构。车次票价等信息 因为不会发生经常性的变化,所以应常驻内存,结合cdn分发策略,将用户引导至最近服务器,返回查询信息。余票数据因为涉及到需要和当前售票情况相关,所 以应在每卖出一张票后同步更新缓存数据.这里需要注意的是查询余票只需要读缓存即可,在缓存存在情况下不需要去访问数据库,同步缓存在买票阶段再说明。如 果缓存没有可以直接访问数据库,如果访问分流设计到位,相信缓存穿透情况会比较少,也不会对数据库带来很大压力.

三  按车次进行购票队列设计

  因为国内火车票热度关系,同时并发购票情况会非常突出,目前没有看到12306有队列的排队机制,难道网站设计者想让用户立即购买票么?!这个有些疯狂,这就导致很多人上去点购票,却打不开等等。     

我的想法是按照车次进行队列设计,并在缓存中维护这些请求队列.一个用户过来比如买K51车次,系统应从后端缓存中获取该车次缓存信息,并将该用户id 加入队列末端,设置时间戳,并返回给用户前面有多少人等信息。维护该队列的服务器应作为后端的一层进行设计,防止一个服务器崩溃导致用户无法访问或者直接 穿透到下一层。用户浏览器可以1s为单位像这些缓存层访问,缓存队列接收到后同步更新队列中该用户时间戳。对于长久没有请求的id有过期清除机制.目前一 般火车车厢15-20节左右。YW25G(硬卧)定员一般66人左右,YZ25G(硬座)定员118人,RZ25C(软座)定员80人,RW25(软卧)定员36人。所以每辆火车人数在1000-2000左右.当然买票的人数可能会超过这个数值,每天买同一车次人数也不会是一个天文数字。



四 售票逻辑

 

售票处理只负责从前面的队列中获取买票的用户id,并发卖多少张票视情况处理。这里涉及到铁道部以前的票务系统接口,很多业内人士也反映压力最大的就是该接 口调用。假设并发处理可以容纳20个(这个值因为不是内部人士,无法确认,仅是假设),则取队列中20个id,进行并发处理。就像火车站开通了20个窗口 一样,对来的20个人进行售票服务,直至完成.可能这个过程可以有优化的地方,因为毕竟每个用户花在真正买票的时间不一定,有的短,有的长。买票流程应尽 可能优化,尽量缩短用户购票需要的时间,提高购票速度。 售票处理得到票务处接口返回后,即同步更新该车次余票缓存,返回给用户购票信息。

 

五 票数据库设计 火车票数据库层应该是使用铁道部以前的票务系统,这种底层的数据库就像银行一样不会随便变更,要知道很多银行系统等数据库设计基本是90年代由IBM等那时设计的,猜测铁道部可能也会类似这样。所以作为12306等这样的外层服务,只能调用其底层的接口做一些操作。

再回到上面谈到的票务系统接口,个人觉得应尽量减少该接口的调用。目前火车购票主要有三种渠道,售票窗口(火车站及各售票点)、电话购票、12306。下面 设想可能不是合理,只供大家讨论。如果这三种渠道购票比例为40%,20%,40%(这个比例可能会调整),则完全可以确定网上购票每个车次票数。

如果确定,则可以根据车次进行分库。数据库层包含一些列数据库,每个数据库可能负责多个车次信息,具体车次数量由测试压力确定。售票操作由之前调用统一的票 务接口转为了在各车次数据库操作。当然票务接口也会去访问后端数据库,貌似没有不同,但是这样处理后数据库压力已经分流,网站购票只要关心分配好的这些车 次数据库就好。像热点车次也应该考虑请求会比其他普通车次要高很多情况,采用更高端的服务器进行处理,相信硬件在铁道部应该不缺.

售票层次应处理好对车次票额的同步机制。任何情况下,只允许同一车次同一个人来买,保证票数的稳定.

最后,总结一下自己的思想,主要是靠架构分层思想,将12306层次分为CDN分发层、静态资源处理层、动态资源的缓存层、排队缓存层、售票层及底层按照车次水平划分设计的数据库层,最终思想要靠分流、有效排队机制及车次水平划分设计数据库等减少对系统服务器的压力,同时保证购票的准确性。