掘金 后端 ( ) • 2024-06-18 23:09

全球最大票务系统

自2019年12月12日发售春运首日车票,截至2020年1月9日,12306全渠道共发售车票4.12亿张,日均售票能力达到了2000万张,平均一年售出30亿张火车票,也就是说12306已经发展成全球交易量最大的实时票务系统。

12306发布数据显示,2020年春运期间,40天的春运期间,12306最高峰日网站点击量为1495亿次,这相当于每个中国人一天在12306上点击了100次,平均每秒点击量为170多万次。而全球访问量最大的搜索引擎网站, 谷歌日访问量也不过是56亿次,一个12306的零头。 再看一下大家习惯性做对比的淘宝,2019年双十一当天,淘宝的日活跃用户为4.76亿,相当于每个人也在淘宝上点击300多次,才能赶上12306的峰值点击量。

上亿人口,40天时间,30亿次出行,12306之前,全球没有任何一家公司和产品接手过类似的任务。这个网站是在数亿人苛刻的目光中,做一件史无前例却又必须成功的事情。

历史发展

10年前铁道部顶着重重压力决心要解决买车票这个全民难题,2010年春运首日12306网站开通并试运行,2011年12月23日网站正式上线,铁道部兑现了让网络售票覆盖所有车次的承诺,不料上线第一天,全民蜂拥而入,流量暴增,网站宕机,除此之外,支付流程繁琐支付渠道单一,各种问题不断涌现,宕机可能会迟到,但永远不会缺席,12306上线的第二年,网站仍然难以支撑春运的巨大流量,很多人因为网站的各种问题导致抢票失败,甚至耽误了去线下买票的最佳时机,铁道部马不解鞍听着批评,一次又一次给12306改版升级,这个出生的婴儿几乎是在骂声中长大的。2012年9月,中秋国庆双节来临之前,12306又一次全站崩溃,本来大家习以为常的操作,却被另一个消息彻底出炉,这次崩溃之前,铁道部曾花了3.3亿对系统进行升级,中标的不是IBM惠普EMC等大牌厂商,而是拥有国字号背景的太极股份和同方股份,铁道部解释说3.3亿已经是最低价了,但没人能听进去,大家只关心他长成了什么样,没人关心他累不累,从此之后,铁道部就很少再发声明了。

2013年左右,各种互联网公司表示我行我上,开发了各种抢票网站插件。当时360浏览器靠免费抢票创下国内浏览器使用率的最高纪录,百度猎豹搜狗UC也纷纷加入,如今各类生活服务APP,管他干啥的,都得植入购票抢票功能和服务,12306就这样被抢票软件围捕了。不同的是,过去抢票是免费,现在由命运馈赠的火车票,都在明面上标好了价格,比如抢票助力包,一般花钱买10元5份,也可以邀请好友砍一刀,抢票速度上,分为低快高级光速VIP等等速度,等级越高就越考验钱包。

2017年12306上线了选座和接续换乘功能,从此爱人可以自由抢靠窗座,而且夹在两人之间坐立不安,换乘购票也变得简单。2019年上线官方捡漏神器候补购票功能,可以代替科技黄牛,自动免费为旅客购买退票余票。......

阿里云当时主要是给他们提供虚拟机服务,主要是做IaaS这一层,就是基础设施服务这一层,2012年熟悉阿里云历史的应该都知道,那个时候阿里云其实还是很小的一个厂商,所以不要盲目夸大阿里云在里面起的作用。

技术难点

1、巨大流量,高请求高并发。

2、抢票流量。每天放出无数个爬虫机器人,模拟真人登陆12306,不间断的刷新网站余票,这会滋生很多的灰色流量,也会给12306本身的话造成非常大的压力。

3、动态库存。电商的任务是购物结算,库存是唯一且稳定的,而12306每卖出一张车票,不仅要减少首末站的库存,还要同时减少一趟列车所有过路站的。

以北京西到深圳福田的G335次高铁为例,表面上看起来中间有16个车站及16个SKU,但实际上不同的起始站都会产生新的SKU。我们将所有起始和终点的可能性相加,就是16+15+14一直加到一,一共136个SKU,而每种票对应三种座位,所以一共是408个商品。然后更复杂的是用户每买一张票会影响其他商品的库存,假如用户买了一张北京西的高碑店东的票,那北京始发的16个SKU库存都要减一,但是它并不影响非北京始发车票的库存, 更关键的是这些SKU间有的互斥,有的不互斥,优先卖长的还是优先卖短程的呢,每一次火车票的出售都会引发连锁变化,让计算量大大增加,如果再叠加当前的选座功能,计算数量可能还要再翻倍,而这些计算数据需要在大量购票者抢票的数秒,甚至数毫秒内完成,难度可想而知有多多大。

4、随机性。你永远都不知道哪一个人会在哪一天,去到哪一个地点,而双十一的预售和发货,其实已经提前准备了一个月,甚至几个月,并不是集中在双十一那天爆发的那一天。所以必须要有必须要有动态扩容的能力。

读扩散和写扩散

上面说的动态库存,就比如 A -> B -> C -> D 共 4 个车站,假如乘客买了 B -> C 的车票,那么同时会影响到 A->C,A->D,B->C,B->D,涉及了多个车站的排列组合,这里计算是比较耗费性能的。

那么这里就涉及到了 “读扩散” 和 “写扩散” 的问题,在 12 年的时候,12306 使用的就是读扩散,也就是在扣减余票库存的时候,直接扣减对应车站,而在查询的时候,进行动态计算。而写扩散就是在写的时候,就动态计算每个车站应该扣除多少余票库存,在查询的时候直接查即可。

12306本身他其实是读的流量远远大于写的流量,我个人是认为写扩散其实会更好一点。

Pivotal Gemfire

Redis 在互联网公司中使用的是比较多的,而在银行、12306 很多实时交易的系统中,很多采用 Pivotal Gemfire作为解决方案。Redis 是开源的缓存解决方案,而 Pivotal Gemfire 是商用的,我们在互联网项目中为什么使用 Redis 比较多呢,就是因为 Redis 是开源的,不要钱,开源对应的也就是稳定性不是那么的强,并且开源社区也不会给你提供解决方案,毕竟你是白嫖的,而在银行以及 12306 这些系统中,它们对可靠性要求非常的高,因此会选择商用的 Pivotal Gemfire,不仅性能强、高可用,而且 Gemfire 还会提供一系列的解决方案,据说做到了分布式系统中的 CAP

12306 的性能瓶颈就在于余票的查询操作上,上边已经说了,12306 是采用读扩散,也就是客户买票之后,扣减库存只扣减对应车站之间的余票库存,在读的时候,再来动态的计算每个站点应该有多少余票,因此读性能是 12306 的性能瓶颈

当时 12306 也尝试了许多其他的解决方案,比如 cassandra 和 mamcached,都扛不住查询的流量,而使用 Gemfire 之后扛住了流量,因此就使用了 Gemfire。2012年6月一期先改造12306的主要瓶颈——余票查询系统。 9月份完成代码改造,系统上线。2012年国庆,又是网上订票高峰期间,大家可以显著发现,可以登录12306,虽然还是很难订票,但是查询余票很快。2012年10月份,二期用GemFire改造订单查询系统(客户查询自己的订单记录)2013年春节,又是网上订票高峰期间,大家可以显著发现,可以登录12306,虽然还是很难订票,但是查询余票很快,而且查询自己的订票和下订单也很快。

技术改造之后,在只采用10几台X86服务器实现了以前数十台小型机的余票计算和查询能力,单次查询的最长时间从之前的15秒左右下降到0.2秒以下,缩短了75倍以上。 2012年春运的极端高流量并发情况下,系统几近瘫痪。而在改造之后,支持每秒上万次的并发查询,高峰期间达到2.6万个查询/秒吞吐量,整个系统效率显著提高;订单查询系统改造,在改造之前的系统运行模式下,每秒只能支持300-400个查询/秒的吞吐量,高流量的并发查询只能通过分库来实现。改造之后,可以实现高达上万个查询/秒的吞吐量,而且查询速度可以保障在20毫秒左右。新的技术架构可以按需弹性动态扩展,并发量增加时,还可以通过动态增加X86服务器来应对,保持毫秒级的响应时间。

通过云计算平台虚拟化技术,将若干X86服务器的内存集中起来,组成最高可达数十TB的内存资源池,将全部数据加载到内存中,进行内存计算。计算过程本身不需要读写磁盘,只是定期将数据同步或异步方式写到磁盘。GemFire在分布式集群中保存了多份数据,任何一台机器故障,其它机器上还有备份数据,因此通常不用担心数据丢失,而且有磁盘数据作为备份。GemFire支持把内存数据持久化到各种传统的关系数据库、Hadoop库和其它文件系统中。大家知道,当前计算架构的瓶颈在存储,处理器的速度按照摩尔定律翻番增长,而磁盘存储的速度增长很缓慢,由此造成巨大高达10万倍的差距。这样就很好理解GemFire为什么能够大幅提高系统性能了。Gemfire 的存储和计算都在一个地方,它的存储和实时计算的性能目前还没有其他中间件可以取代。

但是 Gemfire 也存在不足的地方,对于扩容的支持不太友好的,因为它里边有一个 Bucket 类似于 Topic 的概念,定好 Bucket 之后,扩容是比较难的,在 12306 中,也有过测试,需要几十个T的内存就可以将业务数据全部放到内存中来,因此直接将内存给加够,也就不需要很频繁的扩容。

12306业务解决方案

当然在优化中,我们靠改变架构加机器可以提升速度效率,剩下的也需要业务上的优化。

1、验证码。如果说是淘宝啊这种网站,他用这种验证码,用12306的验证码,可能大家都不会用了,对不对,但是12306他比较特殊,因为铁路全国就他一家,所以说他可以去做这个事情,他不用把用户体验放在第一位 。他最高的优先级是怎么把票给需要的人手上。

当然这个利益的确是比较大,所以也会采用这种人工打码的方式,可以雇一批大学生去做这个验证码识别。

2、候补。候补车票其实相当于整个系统上,它是一个异步的过程,你可以在这里排队,后面的话也没有抢到票,后面再通知你。

3、分时段售票。对于抢票来说,瞬时抢票会导致对服务器有瞬间很大的压力,因此从业务设计上来说需要将抢票的压力给分散开,比如今天才开启抢15天之后的车票。2点抢票,3点抢票等等。

总结

只有程序员才知道,一个每天完成超过1500万个订单,承受近1500亿次点击的系统到底有多难,在高峰阶段的时候,平均每秒就要承受170多万次的点击,面对铁路运输这种特殊的运算模式,也能够保证全国人民在短时间内抢到回家的票,12306就是在无数国人的苛责和质疑中,创造了一个世界的奇迹。

12306除了技术牛,还有着自己的人情关怀,系统会自动识别购票者的基本信息,如果识别出订单里有老人会优先给老人安排下铺儿童和家长会尽量安排在邻近的位置,12306 在保证所有人都能顺利抢到回家的票的同时,还在不断地增加更多的便利,不仅在乎技术问题,更在乎人情异味,12306可能还不够完美,但他一直在努力变得更好,为我们顺利回家提供保障,这是背后无数程序员日夜坚守的结果,我们也应该感谢总设计师单杏花女士,所以你可以调侃,可以批评,但不能否认12306背后所做出的所有努力!