由12306.cn谈谈网站性能技术

由12306.cn谈谈网站性能技术

12306.cn网站挂了,被全国人民骂了。我这两天也在思考这个事,我想以这个事来粗略地和大家讨论一下网站性能的问题。因为仓促,而且完全基于本人有限的经验和了解,所以,如果有什么问题还请大家一起讨论和指正。(这又是一篇长文,只讨论性能问题,不讨论那些UI,用户体验,或是是否把支付和购票下单环节分开的功能性的东西)

业务

任何技术都离不开业务需求,所以,要说明性能问题,首先还是想先说说业务问题。

  • 其一有人可能把这个东西和QQ或是网游相比。但我觉得这两者是不一样的,网游和QQ在线或是登录时访问的更多的是用户自己的数据,而订票系统访问的是中心的票量数据,这是不一样的。不要觉得网游或是QQ能行你就以为这是一样的。网游和QQ 的后端负载相对于电子商务的系统还是简单。
  • 其二有人说春节期间订火车的这个事好像网站的秒杀活动。的确很相似,但是如果你的思考不在表面的话,你会发现这也有些不一样。火车票这个事,一方面会伴随着大量的查询操作,更BT的是下单的时候需要对数据库很多的一致性的操作,一方面是从起点到终点各个分段票的一致性,另一方面,买的人路线、车次、时间选择有很多,会不停地改变下单方式。而秒杀,直接杀就好了,没有那么多查询和一致性的问题。另外,关于秒杀,完全可以做成只接受前N个用户的请求(完全不操作后端的任何数据, 仅仅只是对用户的下单操作log),这种业务,只需要在内存cache中放好可秒杀的数量,还可以把数据分布开来放,100商品,10台服务器一台放10个,无需在当时操作任何数据库。可以订单数够后,停止秒杀,然后批量写数据库。而且秒杀的商品不多。火车票这个不是像秒杀那么简单的,春运时间,几乎所有的票都是热门票,而且几乎是全国人民都来了,而且还有转车业务,多条线的库存都要做事务操作,你想想吧,这有多难。(淘宝的双十一也就3百万用户,而火车票瞬时有千万级别甚至是亿级别的)(更新:2014年1月11日:来了淘宝后,对淘宝的系统有了解,淘宝的秒杀活动,本质上是用输验证码并在CDN上把用户直接过滤掉了,比如:1千万个用户过滤了只剩2万个用户,这样数据库就顶得住了)
  • 其三有人拿这个系统和奥运会的票务系统比较。我觉得还是不一样。虽然奥运会的票务系统当年也一上线就废了。但是奥运会用的是抽奖的方式,也就是说不存在先来先得的抢的方式,而且,是事后抽奖,事前只需要收信息,事前不需要保证数据一致性,没有锁,很容易水平扩展。
  • 其四订票系统应该和电子商务的订单系统很相似,都是需要对库存进行:1)占住库存,2)支付(可选),3)扣除库存的操作。这个是需要有一致性的检查的,也就是在并发时需要对数据加锁的。B2C的电商基本上都会把这个事干成异步的,也就是说,你下的订单并不是马上处理的,而是延时处理的,只有成功处理了,系统才会给你一封确认邮件说是订单成功。我相信有很多朋友都收到认单不成功的邮件。这就是说,数据一致性在并发下是一个瓶颈

  • 其五铁路的票务业务很变态,其采用的是突然放票,而有的票又远远不够大家分,所以,大家才会有抢票这种有中国特色的业务的做法。于是当票放出来的时候,就会有几百万人甚至上千万人杀上去,查询,下单。几十分钟内,一个网站能接受几千万的访问量,这个是很恐怖的事情。据说12306的高峰访问是10亿PV,集中在早8点到10点,每秒PV在高峰时上千万。

多说几句:

  • 库存是B2C的恶梦,库存管理相当的复杂。不信,你可以问问所有传统和电务零售业的企业,看看他们管理库存是多么难的一件事。不然,就不会有那么多人在问凡客的库存问题了。(你还可以看看《乔布斯传》,你就知道为什么Tim会接任Apple的CEO了,最主要的原因是他搞定了苹果的库存周期问题)
  • 对于一个网站来说,浏览网页的高负载很容易搞定,查询的负载有一定的难度去处理,不过还是可以通过缓存查询结果来搞定,最难的就是下单的负载。因为要访问库存啊,对于下单,基本上是用异步来搞定的。去年双11节,淘宝的每小时的订单数大约在60万左右,京东一天也才能支持40万(居然比12306还差),亚马逊5年前一小时可支持70万订单量。可见,下订单的操作并没有我们相像的那么性能高。
  • 淘宝要比B2C的网站要简单得多,因为没有仓库,所以,不存在像B2C这样有N个仓库对同一商品库存更新和查询的操作。下单的时候,B2C的 网站要去找一个仓库,又要离用户近,又要有库存,这需要很多计算。试想,你在北京买了一本书,北京的仓库没货了,就要从周边的仓库调,那就要去看看沈阳或 是西安的仓库有没有货,如果没有,又得看看江苏的仓库,等等。淘宝的就没有那么多事了,每个商户有自己的库存,库存就是一个数字,并且库存分到商户头上了,反而有利于性能扩展。
  • 数据一致性才是真正的性能瓶颈。有 人说nginx可以搞定每秒10万的静态请求,我不怀疑。但这只是静态请求,理论值,只要带宽、I/O够强,服务器计算能力够,并支持的并发连接数顶得住10万TCP链接的建立 的话,那没有问题。但在数据一致性面前,这10万就完完全全成了一个可望不可及的理论值了。

我说那么多,我只是想从业务上告诉大家,我们需要从业务上真正了解春运铁路订票这样业务的变态之处。

前端性能优化技术

要解决性能的问题,有很多种常用的方法,我在下面列举一下,我相信12306这个网站使用下面的这些技术会让其性能有质的飞跃。

一、前端负载均衡

通过DNS的负载均衡器(一般在路由器上根据路由的负载重定向)可以把用户的访问均匀地分散在多个Web服务器上。这样可以减少Web服务器的请求负载。因为http的请求都是短作业,所以,可以通过很简单的负载均衡器来完成这一功能。最好是有CDN网络让用户连接与其最近的服务器(CDN通常伴随着分布式存储)。(关于负载均衡更为详细的说明见“后端的负载均衡”)

二、减少前端链接数

我看了一下12306.cn,打开主页需要建60多个HTTP连接,车票预订页面则有70多个HTTP请求,现在的浏览器都是并发请求的(当然,浏览器的一个页面的并发数是有限的,但是你挡不住用户开多个页面,而且,后端服务器TCP链接在前端断开始,还不会马上释放或重要)。所以,只要有100万个用户,就有可能会有6000万个链接(访问第一次后有了浏览器端的cache,这个数会下来,就算只有20%也是百万级的链接数),太多了。一个登录查询页面就好了。把js打成一个文件,把css也打成一个文件,把图标也打成一个文件,用css分块展示。把链接数减到最低。

三、减少网页大小增加带宽

这个世界不是哪个公司都敢做图片服务的,因为图片太耗带宽了。现在宽带时代很难有人能体会到当拨号时代做个图页都不敢用图片的情形(现在在手机端浏览也是这个情形)。我查看了一下12306首页的需要下载的总文件大小大约在900KB左右,如果你访问过了,浏览器会帮你缓存很多,只需下载10K左右的文件。但是我们可以想像一个极端一点的案例,1百万用户同时访问,且都是第一次访问,每人下载量需要1M,如果需要在120秒内返回,那么就需要,1M * 1M /120 * 8 = 66Gbps的带宽。很惊人吧。所以,我估计在当天,12306的阻塞基本上应该是网络带宽,所以,你可能看到的是没有响应。后面随着浏览器的缓存帮助12306减少很多带宽占用,于是负载一下就到了后端,后端的数据处理瓶颈一下就出来。于是你会看到很多http 500之类的错误。这说明后端服务器垮了。

四、前端页面静态化

静态化一些不常变的页面和数据,并gzip一下。还有一个变态的方法是把这些静态页面放在/dev/shm下,这个目录就是内存,直接从内存中把文件读出来返回,这样可以减少昂贵的磁盘I/O。使用nginx的sendfile功能可以让这些静态文件直接在内核心态交换,可以极大增加性能。

五、优化查询

很多人查询都是在查一样的,完全可以用反向代理合并这些并发的相同的查询。这样的技术主要用查询结果缓存来实现,第一次查询走数据库获得数据,并把数据放到缓存,后面的查询统统直接访问高速缓存。为每个查询做Hash,使用NoSQL的技术可以完成这个优化。(这个技术也可以用做静态页面)

对于火车票量的查询,个人觉得不要显示数字,就显示一个“有”或“无”就好了,这样可以大大简化系统复杂度,并提升性能。把查询对数据库的负载分出去,从而让数据库可以更好地为下单的人服务。

六、缓存的问题

缓存可以用来缓存动态页面,也可以用来缓存查询的数据。缓存通常有那么几个问题:

1)缓存的更新。也叫缓存和数据库的同步。有这么几种方法,一是缓存time out,让缓存失效,重查,二是,由后端通知更新,一量后端发生变化,通知前端更新。前者实现起来比较简单,但实时性不高,后者实现起来比较复杂 ,但实时性高。

2)缓存的换页。内存可能不够,所以,需要把一些不活跃的数据换出内存,这个和操作系统的内存换页和交换内存很相似。FIFO、LRU、LFU都是比较经典的换页算法。相关内容参看Wikipeida的缓存算法

3)缓存的重建和持久化。缓存在内存,系统总要维护,所以,缓存就会丢失,如果缓存没了,就需要重建,如果数据量很大,缓存重建的过程会很慢,这会影响生产环境,所以,缓存的持久化也是需要考虑的。

诸多强大的NoSQL都很好支持了上述三大缓存的问题。

后端性能优化技术

前面讨论了前端性能的优化技术,于是前端可能就不是瓶颈问题了。那么性能问题就会到后端数据上来了。下面说几个后端常见的性能优化技术。

一、数据冗余

关于数据冗余,也就是说,把我们的数据库的数据冗余处理,也就是减少表连接这样的开销比较大的操作,但这样会牺牲数据的一致性。风险比较大。很多人把NoSQL用做数据,快是快了,因为数据冗余了,但这对数据一致性有大的风险。这需要根据不同的业务进行分析和处理。(注意:用关系型数据库很容易移植到NoSQL上,但是反过来从NoSQL到关系型就难了)

二、数据镜像

几乎所有主流的数据库都支持镜像,也就是replication。数据库的镜像带来的好处就是可以做负载均衡。把一台数据库的负载均分到多台上,同时又保证了数据一致性(Oracle的SCN)。最重要的是,这样还可以有高可用性,一台废了,还有另一台在服务。

数据镜像的数据一致性可能是个复杂的问题,所以我们要在单条数据上进行数据分区,也就是说,把一个畅销商品的库存均分到不同的服务器上,如,一个畅销商品有1万的库存,我们可以设置10台服务器,每台服务器上有1000个库存,这就好像B2C的仓库一样。

三、数据分区

数据镜像不能解决的一个问题就是数据表里的记录太多,导致数据库操作太慢。所以,把数据分区。数据分区有很多种做法,一般来说有下面这几种:

1)把数据把某种逻辑来分类。比如火车票的订票系统可以按各铁路局来分,可按各种车型分,可以按始发站分,可以按目的地分……,反正就是把一张表拆成多张有一样的字段但是不同种类的表,这样,这些表就可以存在不同的机器上以达到分担负载的目的。

2)把数据按字段分,也就是竖着分表。比如把一些不经常改的数据放在一个表里,经常改的数据放在另外多个表里。把一张表变成1对1的关系,这样,你可以减少表的字段个数,同样可以提升一定的性能。另外,字段多会造成一条记录的存储会被放到不同的页表里,这对于读写性能都有问题。但这样一来会有很多复杂的控制。

3)平均分表。因为第一种方法是并不一定平均分均,可能某个种类的数据还是很多。所以,也有采用平均分配的方式,通过主键ID的范围来分表。

4)同一数据分区。这个在上面数据镜像提过。也就是把同一商品的库存值分到不同的服务器上,比如有10000个库存,可以分到10台服务器上,一台上有1000个库存。然后负载均衡。

这三种分区都有好有坏。最常用的还是第一种。数据一旦分区,你就需要有一个或是多个调度来让你的前端程序知道去哪里找数据。把火车票的数据分区,并放在各个省市,会对12306这个系统有非常有意义的质的性能的提高

四、后端系统负载均衡

前面说了数据分区,数据分区可以在一定程度上减轻负载,但是无法减轻热销商品的负载,对于火车票来说,可以认为是大城市的某些主干线上的车票。这就需要使用数据镜像来减轻负载。使用数据镜像,你必然要使用负载均衡,在后端,我们可能很难使用像路由器上的负载均衡器,因为那是均衡流量的,因为流量并不代表服务器的繁忙程度。因此,我们需要一个任务分配系统,其还能监控各个服务器的负载情况。

任务分配服务器有一些难点:

  • 负载情况比较复杂。什么叫忙?是CPU高?还是磁盘I/O高?还是内存使用高?还是并发高?还是内存换页率高?你可能需要全部都要考虑。这些信息要发送给那个任务分配器上,由任务分配器挑选一台负载最轻的服务器来处理。
  • 任务分配服务器上需要对任务队列,不能丢任务啊,所以还需要持久化。并且可以以批量的方式把任务分配给计算服务器。
  • 任务分配服务器死了怎么办?这里需要一些如Live-Standby或是failover等高可用性的技术。我们还需要注意那些持久化了的任务的队列如何转移到别的服务器上的问题。

我看到有很多系统都用静态的方式来分配,有的用hash,有的就简单地轮流分析。这些都不够好,一个是不能完美地负载均衡,另一个静态的方法的致命缺陷是,如果有一台计算服务器死机了,或是我们需要加入新的服务器,对于我们的分配器来说,都需要知道的。另外,还要重算哈希(一致性hash可以部分解决这个问题)。

还有一种方法是使用抢占式的方式进行负载均衡,由下游的计算服务器去任务服务器上拿任务。让这些计算服务器自己决定自己是否要任务。这样的好处是可以简化系统的复杂度,而且还可以任意实时地减少或增加计算服务器。但是唯一不好的就是,如果有一些任务只能在某种服务器上处理,这可能会引入一些复杂度。不过总体来说,这种方法可能是比较好的负载均衡。

五、异步、 throttle 和 批量处理

异步、throttle(节流阀) 和批量处理都需要对并发请求数做队列处理的。

  • 异步在业务上一般来说就是收集请求,然后延时处理。在技术上就是可以把各个处理程序做成并行的,也就可以水平扩展了。但是异步的技术问题大概有这些,a)被调用方的结果返回,会涉及进程线程间通信的问题。b)如果程序需要回滚,回滚会有点复杂。c)异步通常都会伴随多线程多进程,并发的控制也相对麻烦一些。d)很多异步系统都用消息机制,消息的丢失和乱序也会是比较复杂的问题。
  • throttle 技术其实并不提升性能,这个技术主要是防止系统被超过自己不能处理的流量给搞垮了,这其实是个保护机制。使用throttle技术一般来说是对于一些自己无法控制的系统,比如,和你网站对接的银行系统。
  • 批量处理的技术,是把一堆基本相同的请求批量处理。比如,大家同时购买同一个商品,没有必要你买一个我就写一次数据库,完全可以收集到一定数量的请求,一次操作。这个技术可以用作很多方面。比如节省网络带宽,我们都知道网络上的MTU(最大传输单元),以态网是1500字节,光纤可以达到4000多个字节,如果你的一个网络包没有放满这个MTU,那就是在浪费网络带宽,因为网卡的驱动程序只有一块一块地读效率才会高。因此,网络发包时,我们需要收集到足够多的信息后再做网络I/O,这也是一种批量处理的方式。批量处理的敌人是流量低,所以,批量处理的系统一般都会设置上两个阀值,一个是作业量,另一个是timeout,只要有一个条件满足,就会开始提交处理。

所以,只要是异步,一般都会有throttle机制,一般都会有队列来排队,有队列,就会有持久化,而系统一般都会使用批量的方式来处理

云风同学设计的“排队系统” 就是这个技术。这和电子商务的订单系统很相似,就是说,我的系统收到了你的购票下单请求,但是我还没有真正处理,我的系统会跟据我自己的处理能力来throttle住这些大量的请求,并一点一点地处理。一旦处理完成,我就可以发邮件或短信告诉用户你来可以真正购票了。

在这里,我想通过业务和用户需求方面讨论一下云风同学的这个排队系统,因为其从技术上看似解决了这个问题,但是从业务和用户需求上来说可能还是有一些值得我们去深入思考的地方:

1)队列的DoS攻击。首先,我们思考一下,这个队是个单纯地排队的吗?这样做还不够好,因为这样我们不能杜绝黄牛,而且单纯的ticket_id很容易发生DoS攻击,比如,我发起N个 ticket_id,进入购票流程后,我不买,我就耗你半个小时,很容易我就可以让想买票的人几天都买不到票。有人说,用户应该要用身份证来排队, 这样在购买里就必需要用这个身份证来买,但这也还不能杜绝黄牛排队或是号贩子。因为他们可以注册N个帐号来排队,但就是不买。黄牛这些人这个时候只需要干一个事,把网站搞得正常人不能访问,让用户只能通过他们来买。

2)对列的一致性?对这个队列的操作是不是需要锁?只要有锁,性能一定上不去。试想,100万个人同时要求你来分配位置号,这个队列将会成为性能瓶颈。你一定没有数据库实现得性能好,所以,可能比现在还差。抢数据库和抢队列本质上是一样的

3)队列的等待时间。购票时间半小时够不够?多不多?要是那时用户正好不能上网呢?如果时间短了,用户不够时间操作也会抱怨,如果时间长了,后面在排队的那些人也会抱怨。这个方法可能在实际操作上会有很多问题。另外,半个小时太长了,这完全不现实,我们用15分钟来举例:有1千万用户,每一个时刻只能放进去1万个,这1万个用户需要15分钟完成所有操作,那么,这1千万用户全部处理完,需要1000*15m = 250小时,10天半,火车早开了。(我并非信口开河,根据铁道部专家的说明:这几天,平均一天下单100万,所以,处理1000万的用户需要十天。这个计算可能有点简单了,我只是想说,在这样低负载的系统下用排队可能都不能解决业务问题

4)队列的分布式。这个排队系统只有一个队列好吗?还不足够好。因为,如果你放进去的可以购票的人如果在买同一个车次的同样的类型的票(比如某动车卧铺),还是等于在抢票,也就是说系统的负载还是会有可能集中到其中某台服务器上。因此,最好的方法是根据用户的需求——提供出发地和目的地,来对用户进行排队。而这样一来,队列也就可以是多个,只要是多个队列,就可以水平扩展了。这样可以解决性能问题,但是没有解决用户长时间排队的问题。

我觉得完全可以向网上购物学习。在排队(下单)的时候,收集好用户的信息和想要买的票,并允许用户设置购票的优先级,比如,A车次卧铺买 不到就买 B车次的卧铺,如果还买不到就买硬座等等,然后用户把所需的钱先充值好,接下来就是系统完全自动地异步处理订单。成功不成功都发短信或邮件通知用户。这样,系统不仅可以省去那半个小时的用户交互时间,自动化加快处理,还可以合并相同购票请求的人,进行批处理(减少数据库的操作次数)。这种方法最妙的事是可以知道这些排队用户的需求,不但可以优化用户的队列,把用户分布到不同的队列,还可以像亚马逊的心愿单一样,通过一些计算就可以让铁道部做车次统筹安排和调整(最后,排队系统(下单系统)还是要保存在数据库里的或做持久化,不能只放在内存中,不然机器一down,就等着被骂吧)。

小结

写了那么多,我小结一下:

0)无论你怎么设计,你的系统一定要能容易地水平扩展。也就是说,你的整个数据流中,所有的环节都要能够水平扩展。这样,当你的系统有性能问题时,“加30倍的服务器”才不会被人讥笑。

1)上述的技术不是一朝一夕能搞定的,没有长期的积累,基本无望。我们可以看到,无论你用哪种都会引发一些复杂性,设计总是在做一种权衡。

2)集中式的卖票很难搞定,使用上述的技术可以让订票系统能有几佰倍的性能提升。而在各个省市建分站,分开卖票,是能让现有系统性能有质的提升的最好方法

3)春运前夕抢票且票量供远小于求这种业务模式是相当变态的,让几千万甚至上亿的人在某个早晨的8点钟同时登录同时抢票的这种业务模式是变态中的变态。业务形态的变态决定了无论他们怎么办干一定会被骂。

4)为了那么一两个星期而搞那么大的系统,而其它时间都在闲着,有些可惜了,这也就是铁路才干得出来这样的事了。

更新2012年9月27日 

 Alexa 统计的12306的PV (注:Alexa的PV定义是:一个用户在一天内对一个页面的多次点击只算一次)

本文转载时请注明作者和出处,请勿于记商业目的


关注CoolShell微信公众账号和微信小程序

(转载本站文章请注明作者和出处 酷 壳 CoolShell ,请勿用于任何商业用途)

——=== 访问 酷壳404页面 寻找遗失儿童。 ===——
好烂啊有点差凑合看看还不错很精彩 (107 人打了分,平均分: 4.63 )
Loading...

由12306.cn谈谈网站性能技术》的相关评论

  1. Allen :
    我想你没明白我的意思。我们都知道票是不够的,但票应该是属于大家的,这样网上购票对于一大部分不会操作电脑的人是不公平的,再说直白点就是农民工兄弟,从人口基数上讲他们才是主体。而且这里面很大一部分年龄大了,已经很难学会电脑了。
    我老婆公司有一个跑单的,四十几岁吧,白天就中午一会在公司,他就在那不停的打电话订票,我老婆说现在还没开票,要到下午三点,但他说下午他不在,于是还是一直打电话。我老婆就跟他说,你做哪趟车,我下午帮你订。结果他自己也不知道,他说马上去买票的地方问问。我老婆说不用了,就问他到哪,然后从网上帮他查了,说下午会帮他订,结果总算订到了。
    我想说这样一位,就是打通电话应该也很难订到票吧!不知道现在网上是不是和线下抢一个池子里的票,如果是的话,线上系统做的越好,效率越高,在购票大厅里的人越难买到票。就是这样一个有限的资源如何满足不同群体的公平性,而不是牺牲一个群体,造福另一个群体,而一个弱势的群体并不是由于他们本身造成的。
    大家都在说技术,插一句,不好意思,呵呵!

  2. 陈皓 :
    1)高峰时段意思是一个尖,不是持续。
    2)仓库是集中管理的,这个和车票是一样的。淘宝的库存分担到了每个商户身上。是我没说清?
    3)在放票的时候,12306的有多少人登录,就有多少人查询,就有多少人下单。这是刚需,和上网买东西不一样。难道这也很难理解?
    4)机票的业务能和火车票这种比吗?能做飞机的人本来就少,而且机票全年可以买。代理公司或是旅行团,一次订N张票,批量操作。
    不是我说不清楚,是你一定要挑刺罢了。我没有给12306支招,我谈的是高性能的技术。你的理解能力需要提高。

    表示赞同,另外机票的业务远没有铁路的业务复杂,就像@Shawnone 提到的席位复用的问题,机票在国内可以说基本不存在,同一车次在不同站点可是都要卖票的,也就是说同一车次的车票库存是随时在变化的,这个是业务的难点所在

  3. punyant :
    同一车次在不同站点可是都要卖票的

    没这回事。

    每个站点某车次可以卖几张什么票是有指标的,没指标谁也没法子卖掉。

    淡季在一些小站买票,甚至半个车厢都是空的,但它仍然只敢卖无座票给你,原因就是没指标。我就经常遇到这种情况。好像也就近两年情况才略有好转,不过车票仍然是按指标卖的,只是信息更新略快了一点。

  4. 铁路订票系统虽然逻辑复杂,但是就那么一点点的库存数据,系统性能这么差,作的真是够垃圾的。事前也不做演练和评估,这不是技术问题,是管理问题。

  5. invalid :

    punyant :
    同一车次在不同站点可是都要卖票的

    没这回事。
    每个站点某车次可以卖几张什么票是有指标的,没指标谁也没法子卖掉。
    淡季在一些小站买票,甚至半个车厢都是空的,但它仍然只敢卖无座票给你,原因就是没指标。我就经常遇到这种情况。好像也就近两年情况才略有好转,不过车票仍然是按指标卖的,只是信息更新略快了一点。

    有指标应该是没错,但可销售票也不会是完全不变的,还有席位复用的情况哦

  6. 讨论起来,不错的,从商务讲对于我们的系统,这样的环境极具挑战的;因为只有在中国有这样的实际环境;做好了,推广出去;商机无限啊;现在我们很多的系统在海外拿单就是靠着在国内练兵有了成功的经验的,ibm,oracle怎么着,到哪找这样的实际环境去。。。失败不怕,逐步改进。

  7. 其实……这个车票的仓储还是有自己的特点的……

    1、难点:每张票是可以拆分的,乘客不必非要买到终点站

    2、“易”点:每张票都是不同的,不存在其它仓储系统针对无数花色的数量管理问题。

    比如,超市/购物网站,可能不同的商品有上万种,每种都有许多库存;而车票呢,按每节车厢200人、一列火车30节算,也不过才6000张票。

    一个车站每天按10分钟一趟车算,一共6*12=72趟车;放大点,按100趟算,共100*6000=600,000张票。

    这60万张票,显然还不能让一个车站一下子卖光了;沿途还有许多车站呢。于是分吧,你可以卖10万张,他可以卖8万张,提前10天开卖,每天或每两天根据销售情况调整指标——这就是指标管理。

    指标调整还可以有很多细节。

    如,北京卖了10万张到广州的火车票,其中1万张在武汉下车;那么当天盘点后,武汉就可以额外得到1万张票。

    ——我就经常有这样的经历。本来车上挤的都没地方站;一到夜里2~3点,卧铺下了一大批人,于是过去补卧铺。补完车上再和下游的车站联系,告诉他们余几张卧铺可以卖。这也是“忽然来票”的一种原因。

    那么,这时候卖票显然就应该用“挤牙膏式”策略:提前10天卖票,每天卖6万张;然后在每晚综合售票情况后,把中途下车的情况计算在内,增加下游车站的可用指标;然后根据新的统计结果重新计算各车站的可用指标。

    如果不用这种策略,而是用通过车票管理中心系统随意售票,那么可以想象,肯定每个站都会抢着出票,坐一站都卖。而不是现在这样优先长途。

    显然,谁抢先把票卖了,钱就是谁的。

    但春运最大的问题,是长途旅客的问题而不是短途旅客的问题。

    ————————————————————
    综上,显然,关于车票,和普通的电子商务网站是非常不一样的。

    它的后台负荷显然其实相当低,也不需要搞分布式管理所有的车票;相反,是每个车站自成系统,定时统计然后分阶段调整每个车站的车票指标(这个过程完全可以是自动化的;且在同步数据时可暂停售票——事实上,由于指标用尽,要求停止售票是非常合理、自然的)。

    这样一分析,售票问题就退回单个车站票务信息的管理上了;由前面的分析,单个车站的数据量其实并没多大。

    前面分析过车票的特点,经常做后台的可能已经看出来了:这不就活生生一个内存池管理算法吗?

    随便抓个STL什么的实现来改造下,不需要改几行代码,就可以拿来用在售票上;每张票只需给它一个位表示操作锁定即可,这比一次cache未命中代价都低很多。几百万张甚至几千万张票这种小规模问题,整个算法完全可以跑在内存里;甚至再大点也可以按车票编号/车次之类分布到若干台服务器的内存里——当然,可靠性、容错、灾难恢复等等还是需要的,但这些有成熟的架构,不需要敲代码了。

    当然,这只是解决了车票查询和初步锁定问题;之后的事务性管理(包括下单、记录等等)可通过数据库进行;不过有内存里的那些锁避免冲突,这些事务已经没有锁的需求了;而一旦摆脱运行于数据库上的锁,事务的处理效率恐怕就极其惊人了。

    ——————————————————————

    综上可见,售票问题,后台并不是瓶颈,起码不是特别严重的问题——之前也有网友说过,前台web页面再卡,一旦能进后台流程就很流畅,可能也证明了这点。

    瓶颈是什么呢?
    1、网页服务器本身扛不住
    2、大量的查票请求给后台造成大量不必要的压力,而这种查询请求完全可以用一个不是那么实时的、单独的内存数据库解决——一旦这样做,查票的后台逻辑其实就和简单的echo差不多了。

    ——————————————————
    不过,以上的分析也是在完全没有第一手资料的情况下,根据蛛丝马迹做的一点猜测。或许真正拿到热点数据,才发现情况根本不是那么回事。

    在拿到数据之前,我们所有的分析,大概不过只能算是些过早优化的病态行为罢了。

  8. aqssxlzc :
    我觉得很多人没用过这个网站就来发表意见,这个网站虽然反复的提示用户太多稍后再试,可是绝对没有说挂掉或者慢的情况。只要登陆进去,订单成功提交,后续过程基本没有页面慢死的情况。估计他是直接从请求中抽取一部分人登陆来控制在线用户数量,然后再提交的订单中抽出一部分进入付款来控制售票速度。

    这位兄弟估计是10号以后才用的,10号以后基本是你说的这种情况,那都是崩溃被骂了以后的事情了。

  9. 嗯,总结下:

    1、我不认为有票务中心

    2、即使有票务中心,票务中心也不是卖票的,而是分配指标的

    3、系统架构是: 分为若干自成系统的车站售票点和一个票务中心两个层次。

    4、售票点可看做从票务中心“进货”;它卖出的、未到终点的车票也可以向票务中心“退货”,然后由票务中心“卖”给下游车站。

    5、这个架构决定了,火车售票系统绝不可能是一个“国家规模”的问题,而是一个“地市规模”的问题,甚至只是车站级别的问题——有些城市有很多个车站,这些车站之间并没有联系的必要。

    6、任何从“国家规模”开始讨论的方案,都是需求分析不过关的表现。

  10. 觉得不一定非要从技术上考虑, 其实对于中国国情,铁道部可以用更霸道、更中国国情的手段,来解决分布式事务、缓存、数据一致性、异步、并发这么多五花八门的技术难题;

    很简单, 就是改变一下支付和订票规定流程;
    取消所有网络支付手段;
    1) 你要网上订票和查询, OK, 先去铁道部指定窗口、银行、营业网点购买专门的储值卡, 然后可以通过转账、网络银行、第三方支付等等等,存入保证金,保证金数额100元起, 不计利息;

    2) 你要查询火车票余额, 至少保证你保证金额内有至少100元余额; 阻止了很多无意义的查询, 反正你先要存钱才能用这个网站。

    3) 预订下单火车票时, 每笔订单需要保证金账户内数额大于订购火车票金额,否则拒绝后继处理。用户 一旦确认提交订单, 该笔金额立刻锁定, 选择支付的话, 就不需要等30分钟让用户去考虑怎么网络支付了, 直接从保证金账户中扣除即可。

    4) 即使用户取消订单(运气好的在系统数据库秒扣保证金额和定票事务写入数据库前成功前做到),之前锁定的保证金金额也会被锁定至少24小时后解冻。

    ———–

    *)铁道部解决了大并发事务, 又能占着用户大笔无息现金储值
    *)真心要买票的用户也不在乎自己的现金被铁道部霸占着, 全国这么多打新股的第三方存管账户的, 支付宝内的钱的用户不也这么过来了么。
    *)黄牛想要依靠DoS攻击的队列攻击, 对不起, 先要做好有足够的保证金额被冻结的准备。反正每笔下单, 都是要真金白银被锁定的。即便只是损失利息。

  11. 看了这么多人回复,忍不住想回:说的不是技术层面的,见谅。
    1、首先铁道部的运能就那样,就算12306解决了技术问题,任何人都可以瞬间访问,查询又怎样?还是有很多人买不到票。
    2、网上售票,搞网络的技术牛人多的是,很多人想搞的话,随便就搞几个登录器出来,怎么防范?道高一尺,魔高一丈。
    …… ……
    我倒是觉得铁道部不要搞什么高铁,把一些重要地段铁路搞成双线制多线制,平时人流量小,单线跑,流量大立刻上多线,运能立刻提升,平时也利于检修线路。
    几年没买到火车票回家了,无语啊

  12. 可笑、可怜的论调!业界早有成熟完整的解决方案。为什么不采用????想着肥水不留外人田自己搞,典型的官僚主义。

  13. AREX :
    看了这么多人回复,忍不住想回:说的不是技术层面的,见谅。
    1、首先铁道部的运能就那样,就算12306解决了技术问题,任何人都可以瞬间访问,查询又怎样?还是有很多人买不到票。
    2、网上售票,搞网络的技术牛人多的是,很多人想搞的话,随便就搞几个登录器出来,怎么防范?道高一尺,魔高一丈。
    …… ……
    我倒是觉得铁道部不要搞什么高铁,把一些重要地段铁路搞成双线制多线制,平时人流量小,单线跑,流量大立刻上多线,运能立刻提升,平时也利于检修线路。
    几年没买到火车票回家了,无语啊

    最大的问题是地区发展不平衡。

    哪怕只是各省的省会能有北上广的一半发达,你以为大家疯了,喜欢背井离乡挤火车跑几千里外啊。

  14. 和一个做技术的人对12306的技术进行讨论,他表示对12306的技术表示不屑,我心里想,你初一该环境下,也好不到哪里去。

  15. 至于铁路订票,我认为偏偏不能用什么“业界成熟完整的解决方案”。因为这个情况太特殊了。

    举个例子,北京西-广州,这趟车的车票在北京怎么卖?

    你要敢敞开了供应,首先得知道,河北发展也不咋地,多少河北人上北京了。这些人坐汽车轻松回家;结果一看,网上敞开卖火车票,便宜啊,定吧。

    这一定,火车上一半位置没了;但这些人可能没坐几站就下了。

    真正需要长途票的,如需要到武汉以远的旅客们只能傻眼了:没票,票都给河北人买了。

    除非他们去石家庄或更远地区坐车;但这样他们必须提前走,否则可能因为堵车之类原因赶不上;而河北本地人就没这种压力。

    所以,除非横着再拉5道陇海、竖着再拉7道京广,彻底解决运力问题(开个玩笑^_^),否则就只能通过统筹安排,以某种策略保证长途客优先了。

  16. invalid :
    至于铁路订票,我认为偏偏不能用什么“业界成熟完整的解决方案”。因为这个情况太特殊了。
    举个例子,北京西-广州,这趟车的车票在北京怎么卖?
    你要敢敞开了供应,首先得知道,河北发展也不咋地,多少河北人上北京了。这些人坐汽车轻松回家;结果一看,网上敞开卖火车票,便宜啊,定吧。
    这一定,火车上一半位置没了;但这些人可能没坐几站就下了。
    真正需要长途票的,如需要到武汉以远的旅客们只能傻眼了:没票,票都给河北人买了。
    除非他们去石家庄或更远地区坐车;但这样他们必须提前走,否则可能因为堵车之类原因赶不上;而河北本地人就没这种压力。
    所以,除非横着再拉5道陇海、竖着再拉7道京广,彻底解决运力问题(开个玩笑^_^),否则就只能通过统筹安排,以某种策略保证长途客优先了。

    1)如果使用队列方案的话, 在规定订单有效排队等候队列中(比如24小时内,100000个排队号)相同车次的买长途票的比短途票有优先权

    2)如果采用经济杠杆的话, 把 河北-北京西的车票价格涨到全线(广州-北京)的一半;那短途的人就会考虑汽车客运等手段了。

  17. @陈皓
    这个确实无法理解,
    1)不会说 12306把所有车次都混在一起做库存吧,最起码用车次来做数据切分是完全可行的。所谓的事务,那都不是问题
    2)其次,火车票如果做库存相对容易,因为车票是唯一的,不像商品,所以逻辑相对简单的多。
    3)再次,淘宝商家商品和火车车次完全是两个数量级的问题,一个商家上亿 * 商品 成百上千 亿的数量,但是 车次就算做到座位级别 也就 运力的量级 一天的运力 亿就了不得了 貌似铁道部估计的春运数量级是千万级一天 ,这只能说明系统设计的差。

    总之
    1)认可12306火车票业务的特殊性,抢票,瞬间高峰难以避免
    2)这个和淘宝不是一个数量级的系统,差别100倍以上吧,设计上太糟糕,从前到后各种挫。
    3)所谓的PV都是浮云,如果订票很顺利,pv很快就下去了,所以就不用拿什么pv数据来说了,完全没意义。因为有很大一部分的pv是无效的,刷登陆 刷提交订单 刷票数据,等等等等。

  18. @inx
    这也是一种可行的解决方案,不过系统复杂性太大,而且要改变太多现有的东西。

    而且,短途票价更贵也早已是常识了。这方面的努力,和之前春运提高票价的提议一样,多半只能是吆喝吆喝,然后被愤青骂骂罢了。

    我还是觉得,过去的指标式管理就是非常棒的成功方案,没有任何理由轻易废除(虽然在其它领域,指标式管理造成了无数痛苦……)
    毕竟有长达10天的预订期,在过去没有电脑的情况下,仍然可以通过电话之类极低效的通信手段完成管理;结果现在有电脑有网络了,反而卖不成票了?

    仅此一项优化,所谓的“分布式事务”之类的棘手需求就不存在了;就成了 “单站点少量车票的管理” 和 “与票务中心重新协调退货/进货” 两个独立的步骤了——这个设计对网速、服务器速度的要求就小太多了。

    甚至于,单站点里面,还可以按时间、车次把票分组,然后定时以组为单位与票务中心协商,这样压力就更小了。

    另外,过去是一天重新分配一次指标,现在自动化了,咱10分钟分配一次行不?
    可以说,车票这种问题上,每30分钟甚至1小时同步一次都不耽误什么。

    ——这种大系统,拆分是一种必然被优先考虑的设计思路:N=5级别的背包问题,还有半点NPC问题的“霸气”吗?
    其它复杂度下不会有如此明显的效果,但仍然不可忽视——话说人们为何要搞集群?
    ——13亿人口的春运问题,只要能想办法拆成一个个车站的售票问题,自然就没那么可怕了。

    换句话说,在过去,“指标”这种挤牙膏方案轻而易举的把中国规模的问题变成了车站级别的问题,只是限于当时的通信能力,他们只能每天挤一次;现在,有计算机了,显然仍然可以用这种挤牙膏方案,只是改成每10分~30分钟挤一次。

    偶认为,这个分析应该就是铁路部门正在使用的方案,当然未必是其全部细节。这个方案使得后台票务管理复杂度大减,甚至某种程度上可忽略不计(之前没用网络售票时,什么时候因为电脑系统问题而导致过春运买票难的?)

    ——最优秀的算法/架构,未必只有程序员才能创造;
    ——永远不要藐视其它行业专业人员的智慧,更不要轻言改变。他们永远比你更了解他们要的是什么;尤其是在其它领域的专业问题上,班门弄斧常常只能自取其辱。
    ——仅仅通过“指标”这一鳞半爪的分析,就能把系统简化到这个程度;如果能详细了解他们的整个方案,还不知道能看到多少惊喜呢。这大概就是需求分析被认为是软件开发过程中最重要的环节的原因吧。

  19. 既然已经实现实名制了,完全可以参考飞机票的方式发售;印度很早以前就已经实现了,铁道部归入交通部下,改为铁路局,提前半年全国可订票,直接就解决这个问题了!

  20. @kekexili
    这种变态需求,外面是没有市场的;唯一一个和我们人数差不多的印度,人家老早就实名制了,和飞机票一样,根本没这种问题。

  21. kekexili :讨论起来,不错的,从商务讲对于我们的系统,这样的环境极具挑战的;因为只有在中国有这样的实际环境;做好了,推广出去;商机无限啊;现在我们很多的系统在海外拿单就是靠着在国内练兵有了成功的经验的,ibm,oracle怎么着,到哪找这样的实际环境去。。。失败不怕,逐步改进。

    这种变态需求,外面是没有市场的;唯一一个和我们人数差不多的印度,人家老早就实名制了,和飞机票一样,根本没这种问题。

  22. 海天使 :
    既然已经实现实名制了,完全可以参考飞机票的方式发售;印度很早以前就已经实现了,铁道部归入交通部下,改为铁路局,提前半年全国可订票,直接就解决这个问题了!

    火车票不是现在都实名了么?至少对于一线城市,京沪穗都具备了2代身份证入站检票的能力, 所以这个建议很好, 提前半年就可以订票, 票可以免费改签其他车次, 但是不得转让他人(针对黄牛)。

  23. inx :觉得不一定非要从技术上考虑, 其实对于中国国情,铁道部可以用更霸道、更中国国情的手段,来解决分布式事务、缓存、数据一致性、异步、并发这么多五花八门的技术难题;
    很简单, 就是改变一下支付和订票规定流程;取消所有网络支付手段;1) 你要网上订票和查询, OK, 先去铁道部指定窗口、银行、营业网点购买专门的储值卡, 然后可以通过转账、网络银行、第三方支付等等等,存入保证金,保证金数额100元起, 不计利息;
    2) 你要查询火车票余额, 至少保证你保证金额内有至少100元余额; 阻止了很多无意义的查询, 反正你先要存钱才能用这个网站。
    3) 预订下单火车票时, 每笔订单需要保证金账户内数额大于订购火车票金额,否则拒绝后继处理。用户 一旦确认提交订单, 该笔金额立刻锁定, 选择支付的话, 就不需要等30分钟让用户去考虑怎么网络支付了, 直接从保证金账户中扣除即可。
    4) 即使用户取消订单(运气好的在系统数据库秒扣保证金额和定票事务写入数据库前成功前做到),之前锁定的保证金金额也会被锁定至少24小时后解冻。
    ———–
    *)铁道部解决了大并发事务, 又能占着用户大笔无息现金储值*)真心要买票的用户也不在乎自己的现金被铁道部霸占着, 全国这么多打新股的第三方存管账户的, 支付宝内的钱的用户不也这么过来了么。*)黄牛想要依靠DoS攻击的队列攻击, 对不起, 先要做好有足够的保证金额被冻结的准备。反正每笔下单, 都是要真金白银被锁定的。即便只是损失利息。

    这个方法很霸道,但是适合中国国情!铁道部既然做电子商务了,不妨连第三方支付也一起做了,弄不好还有机会才成为中国最大电商和第三方支付平台呢。

  24. 实际上查询和提交订单本来就是没有同步的,你们没有碰到过查询后,进入后,提交订单的时候提示没票吗?查询10分钟更新一次,这是有公开信息的。

    前面所有的查询都可以很容易的分开,也完全不用考虑什么同步问题,关键就是提交订单那一下子,麻烦是麻烦点,如博主的很多分析,能搞定最好(今年肯定不可能了,改了现在也不敢上线),改不好个人认为排队还是最好的办法,就跟电话订票一样,其实大量的操作是浪费在一次又一次的提交,失败,提交失败上的。

    排除恶意用户,大量正常用户的操作也就几分钟的事,就那么几趟车可选,麻利的回答没票,谁没事老重试?可您回答不好意思,没空,待会儿再来,大家可不只好重刷了呢?再把退票放出时间固定,刷也就是去了意义。

  25. 前几天我想了一个问题,如果把12306这个网站做的特别好,解决了并发问题,负载问题,让我们买票不会报错,不需要登录半个小时才打开网页。
    这样的结果就是票5分钟就被抢光了,那么排队买票的人怎么办,电话订购的怎么办,是不是有失公平啊!

  26. 我觉得这个售票系统更像是股票交易,既需要访问数据库,实时性又很重要.

  27. AREX :
    看了这么多人回复,忍不住想回:说的不是技术层面的,见谅。
    1、首先铁道部的运能就那样,就算12306解决了技术问题,任何人都可以瞬间访问,查询又怎样?还是有很多人买不到票。
    2、网上售票,搞网络的技术牛人多的是,很多人想搞的话,随便就搞几个登录器出来,怎么防范?道高一尺,魔高一丈。
    …… ……
    我倒是觉得铁道部不要搞什么高铁,把一些重要地段铁路搞成双线制多线制,平时人流量小,单线跑,流量大立刻上多线,运能立刻提升,平时也利于检修线路。
    几年没买到火车票回家了,无语啊

    这个兄弟几年没回家 真是辛苦啊

  28. 天堂的隔壁 :
    实际上查询和提交订单本来就是没有同步的,你们没有碰到过查询后,进入后,提交订单的时候提示没票吗?查询10分钟更新一次,这是有公开信息的。
    前面所有的查询都可以很容易的分开,也完全不用考虑什么同步问题,关键就是提交订单那一下子,麻烦是麻烦点,如博主的很多分析,能搞定最好(今年肯定不可能了,改了现在也不敢上线),改不好个人认为排队还是最好的办法,就跟电话订票一样,其实大量的操作是浪费在一次又一次的提交,失败,提交失败上的。
    排除恶意用户,大量正常用户的操作也就几分钟的事,就那么几趟车可选,麻利的回答没票,谁没事老重试?可您回答不好意思,没空,待会儿再来,大家可不只好重刷了呢?再把退票放出时间固定,刷也就是去了意义。

    网络购票的目的是让使用者更方便,而不是为了证明自己系统多好而折腾百姓。

  29. 你说领导会只让你显示“有”或“无”吗?
    领导会说:“不能这么做。页面还不够漂亮,再改改。”

    不是没有技术人才,我觉得就是领导瞎指挥的问题。

  30. 购买时使用排队机制行不行呢?也即是,登录后进入排队等待的状态,这时候你只能进行查询,限定能下单的人数以及在规定时间内操作。操作生成订单后退出,进入支付环节慢慢支付。

  31. 感谢站长的文章,分析的比较全面,我认为,在技术层面,肯定是能够比现在网站做的更好,或者完全达到订票网站的需求,只要全国招标,拔出一个各项指标均衡最优秀的即可。——+——我在这里指出一个最最关键,但是文章中没有体现出来的方面。对于火车购票系统,并不是一般库存,其中迟迟不能上线的原因在于【远近出票比例】,这个问题才是困扰火车出票的根本原因,举个例子:如果出【上海->苏州】票,必然影响【上海->南京】的票,所以,当没有处理好这个关键因子,在求大于供的系统里,电子购票将是一个很有挑战的目标。

  32. 博文和评论都很精彩,学习了。
    本来只是想到12306的技术问题,可是看了这里的内容后,才知道,原来这里面还有这么复杂的关联。的确,这个网站,不论做好还是做不好,都会有人不满意。但是真要做好了,我觉得平时买票还是会很方便的。对于需要早上坐5点的车进城,然后提着大包小包排半个小时的队买的还不知道是几点的票来说,有了这个系统,对于订出行计划心里就会更有谱了。

Tangbaby进行回复 取消回复

您的电子邮箱地址不会被公开。