由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页面 寻找遗失儿童。 ===——
好烂啊有点差凑合看看还不错很精彩 (106 人打了分,平均分: 4.66 )
Loading...

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

  1. 一些想法,欢迎斧正

    1. 春运的火车票就固定的那么多,并不会像做b2c那样不断的涌进来新的商品,所以总的查询虽然多,其实是可以划分成许多相互之间没有联系的小查询的,比如想坐某个车次的几十万或者几百万人在查询某车次的。

    2. 数据一致性也不是问题,春运这个票量全放内存里并不大,可以考虑采用redis集群模式,实时同步写操作到各个cluster去,所以可以保证每个agent服务那的数据也是即时的。

    3. 不显示票数而显示有或无,这个技术上固然好,不过考虑到天朝的官府公信力,如果只显示有或无,恐怕会引起更大的怀疑,搞不到票的人有可能闹事,网民也会起哄,所以最好还是显示票数,非但是显示票数,最好是哪个身份证买的都显示出来,这样才能消铒民怨。

    4, 我倒是觉得春运这种暴增的任务可以考虑出动飞艇运输,参加博文 http://geek42.info/article/enhancement-of-beijing-traffic.rst

  2. Fenng :
    1. Tim会接任Apple的CEO了,因为他搞定了苹果的库存问题
    这个完全是两回事.
    2.集中在早8点到10点,每秒PV在高峰时上千万
    这个数字有问题
    3.去年双11节,淘宝的每小时的订单数大约在60万左右
    错,淘宝峰值是278万笔/小时
    4. 淘宝要比B2C的网站要简单得多,因为没有仓库 
    这个是臆测。复杂度一样的。
    5. 数据镜像
    至少对于Oracle来说,目前无法做到有效的镜像,镜像更多是为了数据安全可靠
    6. 数据一致性才是真正的性能瓶颈
    数据一致性并非业务难题,并发能力支撑才是难题

    还是冯大辉老师说的实在. 为了突出系统复杂性, 把其他的业务看得太简单了.

  3. 1. 12306其实并不是那么烂,12306高峰期的表现跟加三倍服务器的京东比还是要好不少的,淘宝那次1元卖支付宝宝令的时候,挂的比12306还难看。
    2. Redis、分表分库、排队队列什么的,这些东西知道是做什么的是一回事,敢用到线上去另一回事,特别是这么大的压力,淘宝为了去O,去小型机而投入的研发力量以及积累下来的经验,我相信比12306的合同还要贵的吧。而且这些东西解决的问题对于一个不差钱的主来说其实也算不了什么大问题。
    3. 12306如果真的只用了几千万的话,说实话真的不算贵,建一公里高铁花的钱都比这要多。
    4. 看到一帮子人骂来骂去的,看到很多人的评论内容,感觉有些人的水平可能还不如12306的开发人员。
    5. 12306有了总比没有好,而且身边很多人包括我自己确实在上面买到了票,虽然刷了无数次才登进去,这已经是一种进步了,可是还是很多人在喷,我们部门喷的最厉害的两人,一个是坐飞机回家的,一个是坐汽车回家的,他们都是“听别人说买不到票”,这个怎么回事呢。

    1. 还是你实在啊,一群人在评论里侃侃而谈,给他一个机会真正能做的有几个?

  4. 3)峰值278万没有问题,我还是说了,淘宝的系统比较简单,因为没有他库。不过淘宝当时已经在极限上了,准备关一些省市流量了。

    流量和交易系统的支撑量是两码事,100亿pv刷页面,10亿pv下单,能是一回事么,呵呵。没有仓库,库存还是一样要减的,否则超卖就可以搞死任何一个活动了。

    做技术的,写技术分析文章,还是不要以道听途说来臆测的好。

  5. NinGoo :
    3)峰值278万没有问题,我还是说了,淘宝的系统比较简单,因为没有他库。不过淘宝当时已经在极限上了,准备关一些省市流量了。
    流量和交易系统的支撑量是两码事,100亿pv刷页面,10亿pv下单,能是一回事么,呵呵。没有仓库,库存还是一样要减的,否则超卖就可以搞死任何一个活动了。
    做技术的,写技术分析文章,还是不要以道听途说来臆测的好。

    10亿pv刷页面,10亿pv下单,不好意思,多敲了个0,哈哈

    1. 我就知道最终会在这些细节上有人较真。你远远不明白,几个集中的物理仓库,和各个商户各自的库他的差别。你也就无法理解我说的。

  6. 我觉得,后台用纯C写cgi,前台用ajax减少整张html返回,配合上述的措施,应该会好很多的。

  7. 1. 很多时候无法登陆系统, 我在想这个跟外挂有没有关系, 毕竟一个外挂等于很多人同时登录. 写这个外挂的人该拉出去毙了.
    2. 订单提交比较慢. 这个其实如果真的按照车次来分的话, 似乎数据一致的情况不会那么糟糕. 当然系统怎么设计的不清楚, 表级锁还是行级锁不清楚, 或许只是一个做全局统计的地方存在瓶颈. 这个要详细trouble shooting.

    别的地方似乎没觉得有多少不好的地方, 这个网站可以打分60分了.

    其实, 在社会供给不足的情况下, 还是计划经济, 配给制发票吧. 咱们做不到每个人都有票, 何必让这么多人耗时耗力的争夺呢. 干脆铁道部就提供一个预订系统, 提前一个月提交预订信息, 分级: 例如先保证老人, 儿童, 残疾人, 学生, 按照优先等级来分配, 我们这些年轻人就不要去争夺了. 最后分到票的坐火车, 没有票的自己想办法.

  8. 后台提高性能的,关于技术方向有很多资料,但最关键的还是怎样和业务需求结合。我觉得业务中还漏了重要的一点 – 席位复用。这个对排队,数据一致性等技术都会造成影响。也是和电子商务的订单系统的一个很大不同。

  9. @kino
    我觉得前面如果有几十万人我觉得不算问题,还有队伍肯定在一定的时候是要清空的,一直排着也不符合常理,以前没12306的时候不也是每天早上清空一次吗?

    对变态的业务逻辑只能用变态的业务模型来满足。票(大学)是稀缺资源,不够是正常的。但是只看到有人报怨自己没考好,没报好,没人有抱怨志愿填报不合理。
    当然我们用不着让买票人也去参加考试然后凭分数来排定买票的排队次序。在互联网售票已经可以开始运作(虽然运作的还不完美),我们就把排队的广场放在网络上吧。

    春运期间售票如果能改成以下流程我觉得12306将能满足广大票友的需求
    售票分为以下几个阶段:
    实名注册期 => 需求收集期 => 秒杀抢票期 => 后台卖票期 => 订单确认付款期 => 取票\刷卡上车期

    假定某年春运第一天为1月30号,提前10天(20号)可以拿票来看

    10号(所购票前20天12306开放购票意向收集与用户注册),类似大学报志愿一样,每身份证限购五张,可提交最多所购票当天6个不同车次购票请求,购票请求按优先级从上到下将会在后台售票时依次尝试满足
    18号零点前为购票需求收集截止期。
    18号早上9点开始开放抢票,用户保持已登录状态开始抢位置,系统显示当前排队的位置,刷新没用,越刷越往后排,一次点击就够了!!19号零点前抢票结束 –您要是实在不着急买票,可以等到最后一秒再来抢,不过买不到票您可别怨铁道路
    19号零点后台运行卖票程序,按照18号抢票排队的顺序卖票依次处理售票,售一张则发一个邮件与短信确认,24小时内需确认,逾期则收回。–当然,如果您请票心切,提前把钱存到12306的个人购票账号里也行,铁道部巴不得您在那能存些余额呢。
    20号可拿到纸票,或付款后即可以在铁道部数据库中查到购票记录可届时刷卡上车

    比方说某人今天网速不佳,或选的车次都太热门没买到票,那好,今天没买到30号出发的票,我再修改一下我的车次信息,明天早上9点我再换个网速好点的地方再去抢个好位置,说不定明天他就能如愿买到31的票。

    这个方案有两个“亮”点:
    1)抢票。不抢是不行的,中国人这么多,资源又那么少,铁道运力有限的情况下你能不抢??你不在网上刷网页抢,就得冬天一大早起来排队去抢,或者打电话抢,你选择怎么抢??
    2)后台卖票程序。这个比起什么高并发,数据库瓶颈,多台server,数据库一致性等等问题简单到不知道哪里去了。后台卖票程序运行的时候定在晚上0点到早上7点之间,7个小时其它地方不卖票,光这个程序读数据库中收集好的购票意向信息逐个处理,7个小时处理不完接下来一天的购票信息吗?如果真处理不完那么具体细节设计上我们再好好探讨一下

    排队系统是从云风那里读到的。事在人为,如果不人为设置障碍或者铁道部自己就没打算好好卖,票绝对没有那么难卖

  10. 我觉得问题在于领导,当然了12360的技术人员也都是垃圾。
    1. 一个订票的热点网页,能做出800K大小,需要几十个连接,这完全不可容忍的。如果谁说这都可以容忍,那你不配当程序员。
    2. 订票为何一定要抢?难道不可以以1个小时,或者是1天为一个周期,进行抽签?每个周期,大家随时选票,然后进行抽签,这样即保证了公平性,又可以不用把网站搞垮。
    3. 关于建分站的问题,这个本文作者纯属瞎扯淡。各省建立分站,结果是各个省份自制,且不说制度很乱,而且效果也不会好。因为建立单一的购买渠道,在这个渠道处也可以进行分流,何必让个省市去做?而且,从资源利用率来说,单一的网站可以更合理的利用资源。
    4. 我觉得如果有现有的云计算服务可以用的话,铁道部不应该自己从服务器做起。因为第一,你没法做的比别人更好,第二,你做这么个东西,只是那么几天使用,不值得。如果不信任商业公司,可以签订保密协议,如果泄密,重重地处罚。

  11. 陈皓 :
    我就知道最终会在这些细节上有人较真。你远远不明白,几个集中的物理仓库,和各个商户各自的库他的差别。你也就无论理解我说的。

    不是非要和你在细节上较真,但是做技术的,不较真细节来谈架构就是扯淡。就跟你说“集中在早8点到10点,每秒PV在高峰时上千万。”,我不清楚这能持续多少秒?如果持续一分钟,不就6亿pv没了?那一天14亿pv都集中到这几秒了吧。

    仓库库存管理和调度确实是难度,我承认我不懂,所以我也无法去评论说12306或者京东在这一块容易还是难。如果把仓库库存管理调度和下单系统完全耦合在一起设计,那确实是难度非常高的问题。

    一个业务,难还是容易,从不同的角度去看可能是不一样的,如果你先假设了难,然后找论点来支撑,那怎么都是难的。如果先假设了容易,找论点来支撑,发现又没有那么难了。所以辩论难还是易,是很相互说服的。各自的经验会得出不一样的结果。

    12306的pv,有多少部分是在刷登录的?有多少pv是在刷票务信息的?有多少在刷下单的?有多少在刷支付的?不要看到14亿就想当然的认为交易系统压力大,就是高并发交易。我没有做过火车票交易系统,但从机票系统来看,压力基本都集中在搜索票务信息上,真正的下单压力其实不大。各机票代理都要从中航信去获取票务信息,这方面说起来比12306自己一家人来做的难度更大了。

    真想给12306平反或者支招,还是多找些数据,多做些研究,别随便百度一把出来几个数字就引用为论据,像你的博客关于里说不到45岁不写书,我想是一个道理的。

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

      不是我说不清楚,是你一定要挑刺罢了。我没有给12306支招,我谈的是高性能的技术。你的理解能力需要提高。

  12. 学生已经是提前了。一般高校都会提前20多天通知回家的学生订票的。主要是硬座票。但是学生里面还是有部分车次是很难弄到票的。主要是山东和四川。这说明即使是划出一个小部分人群来,在相对集中的几天内,铁道部的运力还是不足。作为学生我知道这个。

    老人和残疾人人数较少,而且有相关的证明文件,确实可以。

    儿童就不好说了,至少在目前来说儿童缺乏像身份证一样的证明去保证系统能够识别这个用户就是个儿童。这个可能需要医院,公安局等多个部门共同改革才可能。重点是家长会放心儿童一人上车????即使能够证明了。这也是个问题。

    当然配额制的话,在供给有限的情况下,那就是巨大的寻租和腐败的空间了。谁先拿票,谁不拿票?即使有个评估标准,不说执行情况和评估所需要花费的资金和人力成本了。那也需要一个十分信息公开透明的机制和设计去保障,至少在目前来说还没有哪个政府部门做到。估计等到财政预算公开的那天,这个就实现了。。。。。所以一旦配额,那就是超级黄牛的时代了。

    所以个人认为还是回到经济学的基本框架中来。
    (1)提高运力,同时建立灵活的火车时刻表。这个就是增加供给。具体数据不是很清楚,但是春运这十几天肯定是峰值,不过平时又没那么多人。如果简单的堆车皮,平时的运营成本太高。所以火车的发车和定价机制就要放开。通过灵活的车次和价格来引导日常需求和降低运营成本。当然一方面要保证每天都有到其他地方的车,同时还要保证低成本,少空车,这个我觉得是一个较复杂的优化问题。重点是买车容易,修路难。提高运力修铁路需要时间。。。。。。

    (2)错峰放假。这个就是减少需求。核心的假期就是除夕及之后的三天。大家都集中在这个时候,必然有搞不到票的。加上学生假期也是那个时候。所以可以考虑规定春节前10天和后10天不出售学生票。让学生的需求在高峰期
    前消化掉。至于其他行业的。老板们行行好,别死拉着员工非得等到春节前两天才让回了吧。。。。
    @Gil

  13. 其实12306不是什么“订票”系统,而是“购票”系统,所以才有那么多同步问题,因为你必须去“抢”当前放出来的那点票,而不能“订”未来放出来的票,也不支持模糊的订票需求。
    另外,余票查询,车次查询等等都不应该是数据库同步的,所以实际上同步的只有“占库存”这个操作,但也可以设计成把需求放入按每日车次的队列,先完成支付的先得,异步处理队列再通知用户就可以了。这样基本上就没有用户可以参与的同步事件,准备好足够的服务器就可以解决全人类的订票要求了吧。

    1. 对于淘宝,我不怀疑,还是我说的那句话,没有淘宝没有仓库,没有中心库存,所有的库存都分担到了每个商户头上,所以可以有那么高的并发。淘宝的难度是那些图片,而不是交易量。

  14. 1:数据层:高并发下对数据存取的压力通过分区化来实现,扩展性由读远大于写的特性则增加copy来处理读请求,数据一致性就用单写来保证。
    2:业务逻辑上:只要放票方式是如此,必然带来购票人群在放票瞬间的高峰涌入,除非已经做好系统负载能力等于峰值的准备,否则只能通过削峰的形式处理,比如排队系统。
    3:支持大辉的前5点,第六点实际上说的跟陈皓说的差不多是一码事,平行扩展的时候在保证可用性的前提下,并发能力的提高需要通过增加数据的copy来实现,同步报文丢失的可能性增大,一致性就会受到影响,强制保证一致性必然带来并发能力的下降,不知道我对CAP的理解对不对…

  15. “我想还是要经过这些问题的洗礼后自然就会了,我就是这样系统搞挂几次, PM下台几个就会了,反正怎么换也不会换掉技术人员。”

    一般都是PM把技术人员折腾到死,技术人员纷纷不干了,以至于项目干不小去了,才轮到PM下台吧。

  16. O(∩_∩)O哈哈哈~。。文章和评论都很精彩。。其实吧。。这样做这样一个系统。。本来就是一个极其复杂的工程。。根本就不是一个容易的事情。。周围很多人都在骂。。但是其实这有这样一个东西,已经是很大的进步了。。

  17. 分析的比较有道理,小结的3) 4) 是核心。 归根结底,车票在春运期间的供求关系以及现在的业务模式决定了这个系统,怎么做都有问题。冯大辉的骂泄愤可以理解,不过这个系统并不像大辉先生想像的那么简单。换个业务模式,如果和机票的时候提前很久就能订票,而且纯实名,这个峰值是不是会平均些?

  18. 看到这一段就看不下去了:
    “我看了一下12306.cn,打开主页需要建60多个HTTP连接,车票预订页面则有70多个HTTP请求,现在的浏览器都是并发请求的。所以,只要有100万个用户,就会有6000万个链接,太多了。一个登录查询页面就好了。把js打成一个文件,把css也打成一个文件,把图标也打成一个文件,用css分块展示。把链接数减到最低。”

    所有浏览器对同一个domain都不会有超过2条并发链接:
    RFC2612
    8.1.4 Practical Considerations
    A single-user client SHOULD NOT maintain more than 2 connections with any server or proxy.

  19. 前面的DNS,CDN,减少http请求听懂了,最后那个排队什么的就不动了,不过前面的讲的很有道理,那样是很有帮助。不过问一句啊,磁盘镜像不是为了保护数据安全的吗??

  20. “静态化一些不觉变的页面和数据”

    应该是

    “静态化一些不常变的页面和数据” ?

  21. 现在对12306系统的讨论都忽略了一个重要的问题,前向兼容性。
    各个火车站、代售点和12306使用的是同一个数据库,所以12306必须兼容老的数据库系统,否则就得在全国的售票点重新部署系统,这显然是不现实的。兼容性要求无疑大大增加了系统设计的难度

  22. 果然在 前端 和 后端 发力,把面补全啦。 总结的功力很深厚啊。 受益很多!

  23. 1、10亿PV有多少是使用软件刷出来或者个人重复刷新而产生的?这个问题需要探讨一下,个人估计会有一半
    2、其实可以提前的提供用户录入信息和用户信息进行采集,不一定就要挤在那么一两天。
    3、是否可以只提供后台服务,由其他的网站进行前端的开发。
    4、其实很多技术上解决不了的问题可以通过商务途径来解决的。

  24. 我觉得,越是复杂问题,越应该简单处理,从这点上说,我同意博主不谈细节的做法。

    不过,在具体问题上,我觉得太多人都太局限于某个局部了——比如说,博主就有点过于侧重对云风方案的批评,疏于方案的层次化和模块化了。

    对主持设计过一定复杂度项目的,这篇文章是很棒的,因为他们自己心里有“谱”,知道哪里是哪里,会自己取舍。

    但对一般人,就可能导致大家的关注点分散,甚至不同层次缠杂不清,永远说不出个所以然来。

    我认为,这个系统大概分为如下部分:

    1、web UI
    web UI大概是最简单的部分,大不了加服务器就是。当然,通过一些技术提高其效率是必要的。

    2、boss
    这个没什么可说的,没什么难度。

    3、仓储
    这里就是车票信息。难点是任何一个地方卖的票都来自同一个仓库;而这个仓库可能必须以分布式来支撑高访问压力。那么这时候它的设计就是一个关键点。

    4、以上三个模块间的接口
    模块间必然有速度(吞吐量)不匹配问题。

    比如仓库能支撑100万并非,而网页服务器全国各地几百个城市都各自布有若干组。
    假设“只有”一万台很垃圾的服务器,哪怕每台网页服务器只能支持1000并发,加起来都可以造成一个1000万的峰。

    大局上说,问题无非三个方面:
    1、开源
    开源的意思,就是解决仓储系统瓶颈,使其可支持更高的并发访问。

    2、节流
    节流的意思,就是尽量减少甚至避免无效访问。

    3、削峰
    削峰的意思,就是避免高峰情况下过高的并发访问造成DDOS。

    至于黄牛、倒票、信用、攻击等等问题,和技术无关。

    云风的排队方案,针对的是2和3;博主的方案,更侧重于1。

    ————————
    此外,铁路部门对于车票,也有自己的管理方法。一般是给每个车站多少指标,只允许卖指标内的(当然,经常坐车的都知道,旅客的对策是买短途再补长途,或者买硬座补卧铺,不过这就和问题无关了)。这实质上就在相当程度上消除了部署于不同地区的“仓储”之间同步的需求。

    也正是这种指标式管理,才会出现“定时汇总统计后,把某些地区的多余指标匀给其它地区”的情形,这可能就是“突然放票”的由来。

    考虑这种管理模式,或许仓储问题并没有想象那样困难。

  25. @aqssxlzc
    前提就是step1就完不成。除了网站刚出来的时候(还没到春运高峰)能查询和订票成功。之后就没有成功的查询出来过余票。尝试了用外挂的方法和人肉的方法都不行。难得能进的去的时候已经至少是下午或者晚上了。

  26. 技术上是一个问题,但是如何保证售票的合理公平性也是一个难题。

  27. 顺着文章读,感觉似乎找到解决方案了,可是还原到到细节上,感觉很模糊,很多原理上的东西,实现上着实不清楚。

  28. 是因为网站服务器和设计不行,才导致每人刷几百次,才有十亿的点击的;而不是先有十亿,才把服务器搞崩的。铁道部现在天天拿10亿说事,很想一拳打过去。

  29. 这个真不只技术的问题,浏览器兼容很难吗? 兼容个最基本的火狐和chrome都不行吗? 不管哪里教学说类型这样的系统第一课都是扣钱出票,否则整个事物回滚吧! 这不是技术问题,而是铁道部找了一堆垃圾外包来糊弄事! 我也在小公司混过,每次接政府的活都是开几倍的发票,实际网站则是价钱最便宜的那个! 铁道部系统不难做,这么久了有在多的问题也能解决完! 又不是商业网站天天需求变更!

  30. 呵呵,看来还是很多人关注呀。

    对楼主的大部分观点我表示赞同,分析很到位。 我不去纠结某些数字是否真是,是否夸大,那只是一个数字,一个游戏而已。

    唯一和楼主观点有一点儿出入的是,我认为12306的瘫痪不是前端的问题,不管是 60 链接也好,还是 16 个链接也好,都是静态的,都是cdn扛着的,至少12306瘫痪的时候,静态的这部分还能正常访问。主要慢还要归咎于后端,不管多少人抢票,铁路都要保证自己的内部系统不跨,内部系统和互联网是完全隔离的,可能只有12306这么一个出口,那么必然会对这个 12306 到内网系统的请求数做严格的限制,这就必然导致了 12306 的大量阻塞以至于瘫痪。

  31. 文章很好,对我这种菜鸟有很大的启发。其实谁都知道12306这个系统最大的瓶颈在后端数据库,但是这是没办法的,因为12306这个网站和火车站售票窗口以及各大代售点售票系统使用的是同一个数据库,这个售票系统可早就有了,也在实践中证明了可靠性,可能一时半会还升级不了。12306的程序员不可能那么烂的,他们可能也是有心无力,毕竟整个售票系统的升级不是他们能说了算的。因此最近提出的优化措施只是把支付和订票系统分开,这也是他们唯一能做的了。目前唯一可行的办法是把铁道部把网上售票的数据库和火车站售票的数据库分开,专门拿一部分票在网上卖,这样楼上各位老大提到的优化技术就都可以上了!

  32. @NinGoo @NinGoo 我比较赞同你的观点,我是一名web程序员,资历不深,但是我亲身经历过网上订票,首先,铁道部的网上订票平时很方便,我在北京工作,家在山东,有时候回家买来回的票方便了很多,还有给我父母买过。但是,刚刚过去的几天,我订春节回家的票,说说遇到和看到的问题,
    1,先说登录,我粗略统计了一下,用火狐记录密码最快一分钟能试5次吧。我3天登录进去了8次总共,总共加起来耗时加起来有2-3个小时。t_t
    2,再说查询,我为什么登录了8次,我买从北京到潍坊的车票,有几趟车三天的时间余票数总是一张两张的,我总是希望买一张动车,直接到家的,但是每次提交订单都提示“同时订票人数过多”,不能成功,最终我买了北京-济南(网上),济南到潍坊(电话)的车票
    3,支付,北京到济南的票十分充足,所以我订成功了,但过程花费了我将近半个小时,提交了订单后网页就404了,我有登录了若干次才进去,然后找到未完成的订单支付,支付了2次成功。
    我是做web开发的,工作时间不久,没什么很广的见识,我对上面我所见到的问题说说看法
    1,一列火车最多不会超过4k张票,而且不同车次之间的余票是没有关系的,所以订出一张减一张票我认为应该不难,这样
    我那几天用的时候,‘余票查询’是不能用的,只有登录进去后才能用,我想,如果有了良好的的余票查询那么点击量会下来1/3,我遇到的几天同一趟车票数总为一两张不变,估计要么cache的问题,要么是发生了死锁,如果余票查询能正确的返回结果,没票我不登录那么多次,不会抱有侥幸心理。
    2,支付要跳转到网银,很不方便不可靠,如果能够预存一些钱进去,我想支付应该能快一些。

    铁道部想解决春运问题的确很难,运力不足,我理解,但是网站能做的更好,我订不到票就订不到是了,不要等我登录了几百次之后告诉我没票,是,这比排了几天队被售票处告知没票好w倍,为什么不做的更好呢。

  33. 铁道部一天最多也就发送旅客600w人嘛所有这些票都是从网上买的,加上查询,支付什么的,我也觉得不是很多

  34. Lv :
    看到这一段就看不下去了:
    “我看了一下12306.cn,打开主页需要建60多个HTTP连接,车票预订页面则有70多个HTTP请求,现在的浏览器都是并发请求的。所以,只要有100万个用户,就会有6000万个链接,太多了。一个登录查询页面就好了。把js打成一个文件,把css也打成一个文件,把图标也打成一个文件,用css分块展示。把链接数减到最低。”
    所有浏览器对同一个domain都不会有超过2条并发链接:
    RFC2612
    8.1.4 Practical Considerations
    A single-user client SHOULD NOT maintain more than 2 connections with any server or proxy.

    不要照搬RFC,RFC不知道哪个年代的。实际测试下就是了。 现在的浏览器FF,chrome之类的确实可以同时有很多并发连接。

  35. 对于纠结于内部系统的,是不是可以反过来想?
    铁道部汇总后导出一份数据,给12306,今天能给的票和车次都在这了,就这么多,看着办,卖完为止。
    那么内部系统的瓶颈还有问题没?

  36. 我来说说技术之外的东西吧。

    说实话,春运的火车票最大的问题在于业务需求本身。

    把火车票定成一口价本身就不合理。主要是火车票的真实价值和价格差得太远。这也是为什么以前黄牛党禁不住的最根本原因。

    只要把火车票定成拍卖形式,所有问题全部解决。保证火车票黄牛从此消失。

    以前是因为拍卖不容易操作。现在既然网上订票了,拍卖也就很容易实现了。

    比如说有一百张票。

    只要设成限时拍卖,也就是之前大家各自出价,然后到截止时间,票卖给价格最高的前一百人即可。

    要选择多个车次的话就提交自己意愿列表,比如A票100元,B票90元,C票80元。如果买到则后面的作废,如果前面没买到,自动参考下一个。

    也许会有人说这成了有钱人的游戏,但这个方法比现在的形式提高了很多社会效率。比如同样是给汽车上牌照,在上海就是拍卖,而在北京就是摇号。我觉得还是上海的效率高些。

  37. @Shawnone
    非常赞成这个,铁路客票系统的业务远比我们想象的要复杂,往往从简单的电子商务系统的架构上去看12306,大都没有看到背后复杂的业务,其实12306能做到目前这一步也算是不错了

发表评论

您的电子邮箱地址不会被公开。 必填项已用*标注