12306.cn 网站挂了,被全国人民骂了。我这两天也在思考这个事,我想以这个事来粗略地和你们讨论一下网站性能的问题。由于仓促,并且彻底基于本人有限的经验和了解, 因此,若是有什么问题还请你们一块儿讨论和指正。(这又是一篇长文,只讨论性能问题,不讨论那些用户界面、用户体验、或是是否把支付和购票下单环节分开的功 能性的东西)css
甲、认识业务的特殊性
任何技术都离不开业务需求,因此,要说明性能问题,首先仍是想先说说业务问题。html
- 其一,有人可能把这个东西和扣扣或是网游相比。但我以为这二者是不同的,网游和扣扣在线或是登陆时访问的更多的是用户本身的数据,而订票系统访问的是中心的票量数据,这是不同的。不要以为网游或是扣扣能行你就觉得这是同样的。网游和扣扣的后台负载相对于电子商务的系统仍是简单。
- 其二,有人说春节期间订火车的这个事好像网站的秒杀活动。的确很类似, 但 是若是你的思考不在表面的话,你会发现这也有些不同。火车票这个事,还有不少查询操做,查时间,查座位,查铺位,一个车次不 行,又查另外一个车次,其伴随着大量的查询操做,下单的时候须要对数据库操做。而秒杀,直接杀就行了。另外,关于秒杀,彻底能够作成只接受前N个用户的请求 (彻底不操做后端的任何数据, 仅仅只是对用户的下单操做log),这种业务,只要把各个服务器的时间精确同步了就能够了,无需在当时操做任何数据库。能够订单数够后,中止秒杀,而后批 量写数据库。火车票这个岂止是秒杀那么简单。能不能买到票得当时告诉用户啊。
- 其三,有人拿这个系统和奥运会的票务系统比较。我以为仍是不同。虽然奥运会的票务系统当年也一上线就废了。可是奥运会用的是抽奖的方式,也就是说不存在先来先得的抢的方式,并且,是过后抽奖,事前只须要收信息,事前不须要保证数据一致性,没有锁,很容易水平扩展。
- 其四,订票系统应该和电子商务的订单系统很类似,都是须要对库存进 行:1)占住库存,2)支付(可选),3)扣除库存的操做。这个是须要有一致性的检查的,也就是在并发时须要对数据加锁的。B2C的电商基本上都会把这个 事干成异步的,也就是说,你下的订单并非立刻处理的,而是延时处理的,只有成功处理了,系统才会给你一封确认邮件说是订单成功。我相信有不少朋友都收到 认单不成功的邮件。这就是说,数据一致性在并发下是一个瓶颈。
- 其五,铁路的票务业务很变态,其采用的是忽然放票,而有的票又远远不够 你们分,因此,你们才会有抢票这种有中国特点的业务的作法。因而当票放出来的时候,就会有几百万人甚至上千万人杀上去、查询、下单。几十分钟内,一个网站 能接受几千万的访问量,这个是很恐怖的事情。听说12306的高峰访问是10亿页面访问量,集中在早8点到10点,每秒页面访问量在高峰时上千万。
多说几句:前端
- 库存是B2C的恶梦,库存管理至关的复杂。不信,你能够问问全部传统和电务零售业的企业,看看他们管理库存是多么难的一件事。否则,就不会有那么多人在问凡客的库存问题了。(你还能够看看《乔布斯传》,你就知道为何Tim会接任Apple的CEO了,由于他搞定了苹果的库存问题)
- 对于一个网站来讲,浏览网页的高负载很容易搞定,查询的负载有必定的难度去处理,不过仍是能够经过缓存查询结果来搞定,最难的就是下单的负载。由于要访问库存啊,对于下单,基本上是用异步来搞定的。去年双11节,淘宝的每小时的订单数大约在60万左右,京东一天也才能支持40万(竟然比12306还差),亚马逊5年前一小时可支持70万订单量。可见,下订单的操做并无咱们相像的那么性能高。
- 淘宝要比B2C的网站要简单得多,由于没有仓库,因此,不存在像B2C这样有N个仓库对同一商品库存更新和 查 询的操做。下单的时候,B2C的 网站要去找一个仓库,又要离用户近,又要有库存,这须要不少计算。试想,你在北京买了一本书,北京的仓库没货了,就要从周边的仓库调,那就要去看看沈阳或 是西安的仓库有没有货,若是没有,又得看看江苏的仓库,等等。淘宝的就没有那么多事了,每一个商户有本身的库存,库存分到商户头上了,反而有利于性能。
- 数据一致性才是真正的性能瓶颈。有 人说nginx能够搞定每秒10万的静态请求,我不怀疑。但这只是静态请求,理论值,只要带宽、读写性可以强、服务器计算能力够,并支持的并发链接数顶得住 10万TCP连接的创建的话,那没有问题。但在加上数据一致性这一要求,这10万就完彻底全成了一个可望不可及的理论值了。
我说那么多,我只是想从业务上告诉你们,咱们须要从业务上真正了解春运铁路订票这样业务的变态之处。nginx
乙、前端性能优化技术
要解决性能的问题,有不少种经常使用的方法,我在下面列举一下,我相信12306这个网站使用下面的这些技术会让其性能有质的飞跃。web
1、前端负载均衡
经过DNS的负载均衡器(通常在路由器上根据路由的负载重定向)能够把用户的访问均匀地分散在多个Web服务器上。这样能够减小Web服务器的请求 负载。由于http的请求都是短做业,因此,能够经过很简单的负载均衡器来完成这一功能。最好是有CDN网络让用户链接与其最近的服务器(CDN一般伴随 着分布式存储)。(关于负载均衡更为详细的说明见“后端的负载均衡”)算法
2、减小前端连接数
我看了一下12306.cn,打开主页须要建60多个HTTP链接,车票预订页面则有70多个HTTP请求,如今的浏览器都是并发请求的。因此,只 要有100万个用户,就会有6000万个连接,太多了。一个登陆查询页面就行了。把js打成一个文件,把css也打成一个文件,把图标也打成一个文件,用 css分块展现。把连接数减到最低。shell
—— 注:之前拨号链接时代,由于带宽小,大图片没下载完,用户已经关闭了浏览器页面,因此对链接数要求不高,但对一次的下载量要求很高。但如今带宽已经足够, 一次的下载量已经再也不是瓶颈,因此,不能再以过去那种方式处理这个问题,而应该合并文件,尽可能减小请求数,从而减小交互频率,把节约出的交互资源用于网站 和用户的其余更有价值的互动上。数据库
3、减小网页大小增长带宽
这个世界不是哪一个公司都敢作图片服务的,由于图片太耗带宽了。如今宽带时代很难有人能体会到当拨号时代作个图页都不敢用图片的情形(如今在手机端浏 览也是这个情形)。我查看了一下12306首页的须要下载的总文件大小大约在900KB左右,若是你访问过了,浏览器会帮你缓存不少,只需下载10K左右 的文件。可是咱们能够想像一个极端一点的案例,1百万用户同时访问,且都是第一次访问,每人下载量须要1M,若是须要在120秒内返回,那么就须要,1M * 1M /120 * 8 = 66Gbps的带宽。很惊人吧。因此,我估计在当天,12306的阻塞基本上应该是网络带宽,因此,你可能看到的是没有响应。后面随着浏览器的缓存帮助 12306减小不少带宽占用,因而负载一下就到了后台,后台的数据处理瓶颈一下就出来。因而你会看到不少http 500之类的错误。这说明服务器垮了。后端
4、前端页面静态化
静态化一些不常变的页面和数据,并压缩一下(好比gzip,固然,也要考虑压缩和读取的资源消耗问题)。还有一个变态的方法是把这些静态页面放在内存,也就是(/dev/shm目录下),直接从内存中把文件读出来返回,这样能够减小对磁盘的读写数组
注:由于磁盘读写相对于内存读写来讲太慢。
5、优化查询
不少人查询都是在查同样的,彻底能够用反向代理合并这些并发的相同的查询。这样的技术主要用查询结果缓存来实现,第一次查询走数据库得到数据,并把 数据放到缓存,后面的查询通通直接访问高速缓存。为每一个查询作Hash,使用NoSQL的技术能够完成这个优化。(这个技术也能够用作静态页面)
对于火车票量的查询,我的以为不要显示数字,就显示一个“有”或“无”就行了,这样能够大大简化系统复杂度,并提高性能。
6、缓存的问题
缓存能够用来缓存动态页面,也能够用来缓存查询的数据。缓存一般有那么几个问题:
1)缓存的更新方式:也叫缓存和数据库的同步。有这么几种方法,一是缓存过期失效(也就是time out),在通过必定时间后,让缓存失效,重查;二是,由后端通知更新,一旦后端发生变化,通知前端更新。前者实现起来比较简单,但实时性不高,后者实现 起来比较复杂 ,但实时性高。
2)缓存的换页。内存可能不够,因此,须要把一些不活跃的数据换出内存,这个和操做系统的内存换页和交换内存很类似。FIFO、LRU、LFU都是比较经典的换页算法。相关内容参看Wikipeida的缓存算法。
3)缓存的重建和持久化。缓存在内存,系统总要维护,因此,缓存就会丢失,若是缓存没了,就须要重建,若是数据量很大,缓存重建的过程会很慢,这会影响生产环境,因此,缓存的持久化也是须要考虑的。
诸多强大的NoSQL都很好支持了上述三大缓存的问题。
丙、后端性能优化技术
前面讨论了前端性能的优化技术,因而前端可能就不是瓶颈问题了。那么性能问题就会到后台数据上来了。下面说几个后台常见的性能优化技术。
1、冗余数据
容许数据库出现冗余数据,具体方法就是减小表链接这样的开销比 较大的操做,但这样会牺牲数据的一致性,风险比较大。不少人把NoSQL用作数据,快是快了,由于数据冗余了,但这对数据一致性有大的风险。这须要根据不 同的业务进行分析和处理。(注意:用关系型数据库很容易移植到NoSQL上,可是反过来从NoSQL到关系型就难了)
2、镜像数据
几乎全部主流的数据库都支持镜像(replication)。数据库的镜像带来的好处就是能够作负载均衡。把一台数据库的负载均分到多台上,同时又保证了数据一致性(Oracle的SCN)。最重要的是,这样还能够有高可用性,一台废了,还有另外一台在服务。
数据镜像的数据一致性多是个问题,因此咱们要在单条数据上进行数据分区,也就是说,把一个畅销商品的库存均分到不一样的服务器上,如,一个畅销商品有1万的库存,咱们能够设置10台服务器,每台服务器上有1000个库存,这就好像B2C的仓库同样。
注:数据镜像一致性问题其实没那么好解决,平常现实中常常遇到某一后操做失误,须要恢复到前操做,但由于系统自己是自动化处理,每每只注意到数据完整性这种问题,而无法注意到数据对人的意义问题,从而可能这样的一致性机制反而会形成现实的灾难性,因此,还必须设置一条人工的确认操做(能够有多种方式,好比用户的订单提交再次确认),不然数据一致性会使人抓狂
3、数据拆分(数据分区)
数据镜像不能解决的一个问题就是数据表里的记录太多,致使数据库操做太慢。因此,把数据拆分开来。数据拆分有不少种作法,通常来讲有下面这几种:
1)把数据把某种逻辑来分类。好比火车票的订票系统能够按各铁路局来分,可按各类车型分,能够按始发站分,能够按目的地分……,反正就是把一张表拆成多张有同样的字段可是不一样种类的表,这样,这些表就能够存在不一样的机器上以达到分担负载的目的。
2)把数据按字段分,也就是坚着分表。好比把一些不常常改的数据放在一个表里,常常改的数据放在另外一个表里。把一张表变成1对1的关系,这样,你可 以减小表的字段个数,一样能够提高必定的性能。另外,字段多会形成一条记录的存储会被放到不一样的页表里,这对于读写性能都有问题。
3)平均分表。由于前两种种方法是并不必定平均分均,可能某个种类的数据仍是不少。因此,也有采用平均分配的方式,经过主键ID的范围来分表。
4)同一数据拆开存放。这个在上面数据镜像提过。也就是把同一商品的库存值分到不一样的服务器上,好比有10000个库存,能够分到10台服务器上,一台上有1000个库存。而后负载均衡。
这四种分区都有好有坏。最经常使用的仍是第一种。
注:数据一旦拆分存放,就须要有一个或是多个调度来让你的前端程序知道去哪里找数据,这种调度会消耗反应时间,所以,不可分得太细,调度表和拆开来的表大小比例如何?
把火车票的数据分区,并放在各个省市,会对12306这个系统有很是有意义的质的性能的提升。
4、后台系统负载均衡
前面说了数据拆分存放,数据拆分存放能够在必定程度上减轻系统负载,可是没法减轻热销商品的负载,对于火车票来讲,能够认为是大城市的某些主干线上 的车票。这就须要镜像数据来减轻负载。镜像了数据,必然要用到负载均衡,在后台,咱们可能很难使用像路由器上的负载均衡器,由于那是均衡流量的,由于流量 并不表明服务器的繁忙程序。所以,咱们须要一个任务分配系统,其还能监控各个服务器的负载状况。
任务分配服务器有一些难点:
- 负载状况比较复杂。什么叫忙:是核芯处理量高?仍是磁盘读写频率高?仍是内存使用量高?仍是并发数高?你可能须要所有都要考虑。这些信息要发送给那个任务分配器上,由任务分配器挑选一台负载最轻的服务器来处理。
- 任务分配服务器上须要对任务队列,不能丢任务啊,因此还须要持久化。而且能够以批量的方式把任务分配给服务器。
- 任务分配服务器死了怎么办?这里须要一些如即时替用(Live-Standby)或是失效备援(failover)等高可用性的技术。咱们还须要注意那些持久化了的任务的队列若是转移到别的服务器上的问题。
中央分配式:看到有不少系统都用静态的方式来分配,有的用hash,有的就简单地轮流分析。这些都不够好,一个是不能完美地负载均衡,另外一个静态的方法的致命缺陷是,若是有一台计算服务器死机了,或是咱们须要加入新的服务器,对于咱们的分配器来讲,都须要从新刷新。
下游请缨式:由下游的计算服务器去任务服务器上拿任务。让这些计算服务器本身决定本身是否要任务。这样的好处是能够简化系统的复杂度,并且还能够任意实时地减小或增长计算服务器。可是惟一很差的就是,若是有一些任务只能在某种服务器上处理,这可能会引入一些复杂度。 不过整体来讲,这种方法多是比较好的负载均衡。
5、异步处理、 限流阀 和 批量处理
异步、限流阀(throttle) 和批量处理都须要对并发请求数作队列处理的。
- 异步处理:在业务上通常来讲就是收集请求,而后延时处理。在技术上就是能够把各个处理程序作成并行的,也就能够水平扩展了。可是异步的技术问题大概有这 些,1)被调用方的结果返回,会涉及进程线程间通讯的问题。2)若是程序须要回滚,回滚会有点复杂。3)异步一般都会伴随多线程多进程,并发的控制也相对麻烦一些。4)不少异步系统都用消息机制,消息的丢失和乱序也会是比较复杂的问题。
- 限流阀: 这实际上是个保护机制,是在不提高性能和资源消耗的状况下,防止系统被超过本身不能处理量给搞垮了,这样系统看起来会显得稳健!使用限流阀技术通常来讲是对于一些本身没法有效控制或者是须要稳定不中断运行的系统,好比,和你网站对接的银行系统,或者是长城防火墙。
- 批量处理的技术,是把一堆基本相同的请求批量处理。 好比,你们同时购买同一个商品,没有必要你买一个我就写一次数据库,彻底能够收集到必定数量的请求,一次操做。这个技术能够用做不少方面。好比节省网络带 宽,咱们都知道网络上的最大单位传输量(MTU,或者翻译为最大传输单元,就是一次传输的量),以太网是1500字节,光纤能够达到4000 多个字节,若是你的一个网络包没有放满这个最大传输单元,那就是在浪费网络带宽,由于网卡的驱动程序只有一块一块地读效率才会高。所以,网络发包时,咱们须要收集到足够多的信息后再作网络吞吐,这也是一种批量处理的方式。批量处理的敌人是流量低,因此,批量处理的系统通常都会设置上两个阀值,一个是做业量达标,另外一个是自动过期失效,只要有一个条件知足,就会开始提交处理。
因此,只要是异步,通常都会有限流阀机制,通常都会有队列来排队,有队列,就会有持久化,而系统通常都会使用批量的方式来处理。
云风同窗设计的“排队系统” 就是这个技术。这和电子商务的订单系统很类似,就是说,个人系统收到了你的购票下单请求,可是我尚未真正处理,个人系统会跟据我本身的处理能力来限制住这些大量的请求,并一点一点地处理。一旦处理完成,我就能够发邮件或短信告诉用户你来能够真正购票了。
在这里,我想经过业务和用户需求方面讨论一下云风同窗的这个排队系统,由于其从技术上看似解决了这个问题,可是从业务和用户需求上来讲可能仍是有一些值得咱们去深刻思考的地方:
1)队列的DoS攻击。首先,咱们思考一下,这个队是个单纯地排队的吗?这样作还不够好,由于这样咱们不能杜绝黄牛,并且单纯的ticket_id很容易发生DoS攻击,好比,我发起无数个 ticket_id,进入购票流程后,我不买,我就耗你半个小时,很容易我就可让想买票的人几天都买不到票。有人说,用户应该要用身份证来排队, 这样在购买里就必需要用这个身份证来买,但这也还不能杜绝黄牛排队或是号贩子。由于他们能够注册无数个账号来排队,但就是不买。黄牛这些人这个时候只须要干一个事,把网站搞得正常不能访问,让用户只能经过他们来买。
注:可见,除了限流阀机制,还要一个过时机制,外加一些辅助检测手段(好比须要认证的登录)——其实,此处比拼的就是网站和黄牛的硬件实力了,只要可以把门槛提升到黄牛的硬件投入超越边际效益临界点,便可。
2)对列的一致性?对这个队列的操做是否是须要锁?只要有锁,性能必定上不去。试想,100万我的同时要求你来分配位置号,这个队列将会成为性能瓶颈。你必定没有数据库实现得性能好,因此,可能比如今还差
3)队列的等待时间。购票时间半小时够不够?多很少?要是那时用户正好不能上网呢?若是时间短了,用户也会抱 怨,若是时间长了,后面在排队的那些人也会抱怨。这个方法可能在实际操做上会有不少问题。另外,半个小时太长了,这彻底不现实,咱们用15分钟来举例:有 1千万用户,每个时刻只能放进去1万个,这1万个用户须要15分钟完成全部操做,那么,这 1千万用户所有处理完,须要1000*15m = 250小时,10天半,火车早开了。(我并乱说,根据铁道部专家的说明:这几天,平均一天下单100万,因此,处理1000万的用户须要十天。这个计算可能有点简单了,我只是想说,在这样低负载的系统下用排队可能都不能解决问题)
注: 这说法有点问题:首先,一趟车的载人数是有限的,因此并无必要处理1千万用户的需求,能够经过设定提早购票天数,来分流这些人员。但按照春运总十亿人次 来算的话,若是一次能放进10万个,10万×15分钟=25000小时,则须要1042天处理完,若是一次能放进100万,则须要104天处理完,仍是不 够,春运没有那么多天,一次须要放进1000万,才够!
4)队列的分布式。这个排队系统只有一个队列好吗? 还不足够好。由于,若是你放进去的能够购票的人若是在买同一个车次的一样的类型的票(好比某动车卧铺),仍是等于在抢票,也就是说系统的负载仍是会有可能 集中到其中某台服务器上。所以,最好的方法是根据用户的需求——提供出发地和目的地,来对用户进行排队。而这样一来,队列也就能够是多个,只要是多个队 列,就能够水平扩展了。
我以为彻底能够向网上购物学习。在排队(下单)的时候,收集好用户的信息和想要买的票,并容许用户设置购票的优先级,好比,A车次卧铺买 不到就买 B车次的卧铺,若是还买不到就买硬座等等,而后用户把所需的钱先充值好,接下来就是系统彻底自动地异步处理订单。成功不成功都发短信或邮件通知用户。这样,系统不只能够省去那半个小时的用户交互时间,自动化加快处理,还能够合并相同购票请求的人,进行批处理(减小数据库的操做次数)。这种方法最妙的事是能够知道这些排队用户的需求,不但能够优化用户的队列,把用户分布到不一样的队列,还能够像亚马逊的心愿单同样,让铁道部作车次统筹安排和调整(最后,排队 系统(下单系统)仍是要保存在数据库里的或作持久化,不能只放在内存中,否则机器一挂,就等着被骂吧)。
丁、小结
写了那么多,我小结一下:
0)不管你怎么设计,你的系统必定要能容易地水平扩展。也就是说,你的整个数据流中,全部的环节都要可以水平扩展。这样,当你的系统有性能问题时,“加3倍的服务器”才不会被人讥笑。
1)上述的技术不是一朝一夕能搞定的,没有长期的积累,基本无望。
2)集中式的卖票很难搞定,使用上述的技术可让订票系统能有几佰倍的性能提高。而在各个省市建分站,分开卖票,是能让现有系统性能有质的提高的最好方法。
3)春运前夕抢票且票量供远小于求这种业务模式是至关变态的,让几千万甚至上亿的人在某个早晨的8点钟同时登陆同时抢票的这种业务模式是变态中的变态。业务形态的变态决定了不管他们怎么办干必定会被骂。
4)为了那么一两个星期而搞那么大的系统,而其它时间都在闲着,也就是铁路才干得出来这样的事了。
戊、附录:
1、云风方案:
其实铁路订票系统面临的技术难点无非就是春运期间可能发生的海量并发业务请求。这个加上一个排队系统就能够轻易解决的。
原本我在 weibo 上闲扯两句,这么简单的方案,本觉得你们一看就明白的。没想到仍是许多人有疑问。好吧,写篇 blog 来解释一下。
简单说,咱们设置几个网关服务器,用动态 DNS 的方式,把并发的订票请求分摊开。类比现实的话,就是把人分流到不一样的购票大厅去。每一个购票大厅均可以买到全部车次的票。OK ,这一步的负载均衡怎么作我就不详细说了。
每一个网关其实最重要的做用就是让订票的用户排队。其实整个系统也只用作排队,关于实际订票怎么操做,就算每一个网关后坐一排售票员,在屏幕上看到有人来买票,输入到内部订票系统中出票,而后再把票号敲回去,这个系统都能无压力的正常工做。不然,之前春运是怎么把票卖出去的?
咱们来讲说排队系统是怎么作的:
其实就相似咱们去热门馆子吃饭拿号。只不过要防止别人伪造号插队而已。
若是你来一我的(一次 HTTP 请求),我就随机产生一个我作过一些签名处理的号码返回给你。暂时称为 ticket id 。这个 ticked id 是很难伪造的。
系统在内存里开一个大数组(32G 内存够排上亿人了吧),就是一循环队列。把这个 ticket id 放在队列尾。
用户如今拿着 ticket id 向网关发起请求。网关利用一次 hash 查询,在内存中的数组队列里查到它的位置,马上返回给用户。用户的前端就能够看到,他在这个网关(售票大厅)前面还有多少人等着。
这里的关键是,整个队列都在本机的内存中,查询返回队列中的位置,能够实现的比一个处理静态文件的 web server 还要高效。静态文件至少还要去调用文件 IO 呢。静态文件 web server 能够处理多少并发量,不用我介绍了。
同时,前端会控制用户拿着 ticket id 查询队列位置的频率。高负载时能够 1s 一次,甚至更长时间。为了防止用户本身写脚本刷这个请求(虽然没有太大意义,由于刷的多也不会排到前面去),若是见到同一个 ticket id 过于频繁的查询。好比 10s 内查询了 20 次以上。就直接把这个 ticket id 做废。持有这个 ticket 的人就须要从新排队了。
对于最后排到的人,系统会生成一个惟一的不可伪造的 session id ,用户下面就能够经过这个 session id 去作实际的购票流程了。能够连去真正的购票服务器,也能够经过网关中转。非法的 session id 会马上断掉,用户一旦知道伪造 session id 几乎不可能,只有经过 ticket id 排队拿到,除非是恶意攻击系统,否则不会有人乱拿 session id 去试。
咱们再给每一个 session id 设置一个最长有效时间,好比半小时。若是超过半小时尚未完整购票流程,那么就从新去排队。
至于同时开放多少个 session id ,也就是至关于开放多少个购票窗口,就取决于购票系统能承受的负载了。不过简单计算一下,就知道有排队系统保证了良好的次序,再以计算机的吞吐能力,解决 不过几亿人的购票请求,即便这些人都同来排队,也就是一组机器几小时的处理量而已。
这票 blog 也就是随便写写,可能不太严谨,但意思达到了。中间有不少数据须要估算,也不是太难的事情。
为何如今的购票系统这么滥?关键在于大量的网络带宽,计算力浪费在了“维持次序”上。系统不稳定时,大量的只作了一半的无效的购票流程浪费掉了这 些。要响应高并发的 HTTP 请求,关键就在于迅速反应,不要什么都想着从数据库绕一圈。排队的队伍维持就彻底不须要使用数据库。若是全部 HTTP 请求都马上返回,在短期内能够处理的 HTTP 请求量也会很是大。而若是你一下处理不了这个请求,又把 TCP 链接保持在那里,就莫怪系统支持不住了。
另外,用户看到了不断在减小的队列前面的人数,他们也会安心等待。只要网站页面刷新流畅(只处理队列信息很容易保证),用户体验会很好。
最后补充几句废话:由于铁路购票系统不少年前就实现了内部网络化,有成熟系统支撑,运做多年。此次作互联网版本,必定不能放弃原有系统新来一套。否则实体购票点也在网页上刷不出票就崩溃了。
因此要作的仅仅是怎么作一个系统和原有系统对接。这样风险最小。两套系统能够分别优化处理能力。基于这个设计起点,因此我才不看好全部企图取代原有系统的方案。
再补充:
一、、排队前把身份证填好,系统编码到 ticket 里去。
排到了这个 ticket 只能够用这个身份证买一张或几张同车次的票。
注册环节彻底能够免了。若是须要注册,填个密码就 OK。用户名能够暂时用身份证,买完票能够换,或者去别的服务器上换,不干扰购票流程。
你 8 点出票能够 7 点开始排。系统反正认定你了没人插队。现实就如此。不要去想比一样跟你同样须要票的人更有优先权。
======================================================================
2、其余网友见解:
一、唉,一个网站在大数据量访问是挂掉,是很正常的现象,有什么好炒做的!并且数据量不是几万,而是几亿!!!挂掉不丢人!!!!!
二、订票其实还有一个特色:各车次的分管铁路局是固定的!若是按铁路局将系统分割,负载将大大分散。就如10086.cn同样,他会要求你先输入手机号后跳转到各个分公司的系统。——但这样异地购票或者使用异地手机的就麻烦了!
三、“网游和扣扣在线或是登陆时访问的更多的是用户本身的数据”,非游戏从业者,不过感受游戏里某些公共事件都是在读写公共资源,有时候规模也不小呢;不过副本技术确实极大的缩小了这个规模。
四、文中提到能够使用缓存,好比如今12306是10分钟更新一次,缓存每10分钟都会失效,刷新缓存带来的峰值要怎么处理?
五、用排队+配额申请制,
一句话“更新”(update)转换成“插入”(insert)操做, 非要说像的话, 这像个广告系统
六、放票模式不作更改,运力不提高,单靠优化网站仍是被骂。
假设铁道部有能力优化,那么能够预见,在很短的时间内,票就被卖出去了,没有买到票的人就会骂铁道部,票都被黄牛买了。
七、 我以为须要注意一点火车票和电子商务的不一样,车次是有限的,不像淘宝这样商品无限多,并且业务逻辑简单得多。这 样能够很容易的按照车次分区,每一个车次是单独的队列,甚至彻底能够放在内存,不一样车次间不存在锁的问题,即便同车次也能够优化锁粒度。另外,查询结果没必 要可刻意要求和实际票务状况同步一致,能够偏差一秒钟,查车次和查余票分开,这样90%的查询时能够缓存的。假设车票在开票10分钟内就被抢完, 根据每列车的座位数和每一个座位被卖的次数,能够估算每一个车次,每秒要处理几个请求,实际也没几个。
另外不一样于电子商务的库存,每一个座位能够被卖几回,用位运算实现很简单。
人们之因此用外挂刷票,由于网站烂,登录难,下单难,付款难等等,若是订票过程流畅,外挂天然就没人用了,再加上简单可靠的防刷新和限流措施,应付如今的量没问题,铁道部的数据不可信,看他们说他们处理的是世界顶尖的难题就知道。
八、 技术方面不懂,不过我认为对于12306网站的库存管理,和普通B2C的电商库存管理,有不少不同的地方
(1). 春运期间,运力不足,订单始终大于库存,但库存是固定的,即车票的最大限额。
(2).闲暇时间,订单小于库存,但车次固定,座位固定,库存也是固定的,即使是一个乘客,也会开一班列车
铁道部几乎不用考虑订单和库存之间的供求关系,普通电商过于看重库存管理,是由于惧怕库存积压过多,直接影响到企业资金链的运行,而这个问题对于铁道部根本不存在,铁道部只要考虑怎么把固定的车票数量快速,流畅,均匀的分发到每位乘客手里就能够了。
并且我感受,这么多年应付春运的经验,铁道部对于减轻售票压力的方案也会很成熟的,不过确定都没有用在这个网站上面上。
九、由于无票了,用户不甘心,反复刷——访问压力大10倍
由于网页慢、死,因此用户不停的刷——访问压力大100倍
这是恶性循环:越是慢,访问量越大
若是,网页流畅了,哪怕仍是无票,就不存在这99倍的刷新了,而只是10倍的刷新
这是良性循环:越是快,访问量反而少了
十、铁老大作了一件费力不讨好的事情,这事情直接拿30%的票给淘宝,30%给腾讯,本身留剩下卖,这一天几千万的点击量是个巨大的广告收入啊,本身不花一个子,冲别人大口要钱,这样本身花了几千万作了个被万人马的东西,不如本身不花钱,轻松拿钱,不行还骂别人。
直接找阿里巴巴或腾讯这样的战略伙伴,垂拱而治,何乐而不为呢?
十一、带宽、核芯、存储都不是紧缺资源,一切的根源在于票太少人太多。一到春运运量能翻好几倍,运力只能提高一倍还不到。更好的系统是必须的,可是买不到票仍是挨骂。
一个能够考虑的办法是:限制300千米之内的短程票,分流到公路客运去,这样能够缓解一点点压力。
最好能依据统计分析来肯定:最小的瓶颈在哪。
十二、查询和下单等操做用到的数据,应该是从铁道部客票系统已有的数据接口来直接获取数据。12306.cn和客票系统共享之前的老数据库。老旧的系统,必然适应不了如今的春运的变态需求。
1三、票量=票量-1的时候——那么是否是隐含这个意思:针对某个车次有一条数据库记录用于登记剩余的车票,致使订购某车次的全部订单都要等待锁定这条记录而且更新?若是是这么设计的, 确实有点那啥。通常来讲一个车次的票量有限,彻底能够用一个内存中的test-and-set(reduce)的操做来进行控制,热门车次的票量很快就会下降到零,后面的订单都test不过去,能够直接拒绝了。
(1)数据库锁不可怕,大量订单都要锁定同一资源才可怕
(2)用数据库的锁来作票量更新和统计,带上事务就要慢好几拍;若是这个票量更新要和每笔订单的事务合并提交,那就更要慢上N倍。
————可是,车次那么多,这样的内存量也会很是惊人!!!!
1四、并发下的数据一致性。放在内存里也同样要上锁作互斥。
1五、我以为不少人没用过这个网站就来发表意见,这个网站虽然反复的提示用户太多稍后再试,但是绝对没有说挂掉或者慢的状况。只要登录进去,订单成功提交,后续 过程基本没有页面慢死的状况。估计他是直接从请求中抽取一部分人登录来控制在线用户数量,而后再提交的订单中抽出一部分进入付款来控制售票速度。————也就是说,自己已经使用了限流阀的方法了。
1六、这样一个系统如何实现相对公平性,特别是此次对于电脑操做不会或较弱的人就不公平。
好比若是系统采用了排队系统后,不从技术角度考虑, 我这里还有几个问题想不通。
(1). 何时开始排队,由于你们知道票是上午十点或下午三点出票。
这个队伍是一直存在,从系统运行时起。仍是天天从新排。
若是天天从新排,在重排的那一刻,还在系统里的人如何理?我想这里大部分人是踢出来,否则重排就没意义了。
若是队伍一直存在,进入的人是能够永远在里面,仍是有策略的踢出来。这里 永远在里面应该不可能。
若是有策略的踢出来,好比过一小时踢出来,那若是我要买的票在三点出票,我会想我要在两点多一点进入系统,
而买票的人应该都是这种想法,那我应该何时开始排队,才能保证我能在两点多一点进入系统,天哪,感受这个无解,绝望!
(2). 假如队伍会在天天清空并在天天三点开始排队,但在排队的那一刻起,领到的号靠前的条件是什么?
应该是手速和网速。若是这样的话,咱们能够看看排在队伍前面的是些什么人:
程序刷的, 游戏高手, IT重业人员, 都市白领, 普通受过教育接触计算机的人 文化低(对应操做计算机的能力)的外来务工人员。
程序刷能够禁,但效果如何就不清楚了。
若是是这样的话,与现有系统比,不一样之处就在于
现有系统在人们试了无数次后,给人绝望
而排队后,一开始就给人绝望,你的号码是1000000,在你的前面还有999999人等待处理。
对于一个具体的会操做电脑的普通人而言:
现有系统:试了无数次,网页崩溃无数次,拼尽了耐心,终于买到了一张票。
排队系统: 放弃吧,你在这买不到票
若是开始排队算做比赛的话,那就是职业运动员和业余运动员的
固然,我绝对不是在说现有的系统好!我只是在说咱们全部的技术都做好了,仍是不能从业务角度实现它的合理公平性。
没事,瞎想的!
1七、因此我以为正确的方法实际上是抽奖,在全部请求中抽奖,中奖的登录入系统,在全部订单请求中抽奖,中奖的进入支付。这样比排队系统公平不少,每一个订单都有中奖的机会。
1八、云风的方案可靠就表如今把现实中的购火车票排队系统经过网络来实现, 效率确定要比现实排队高多了.
先排到队, 而后再查询, 再买票…总体火车购票系统的需求不就是这样吗?
另外, 负载小, 压力小, 实现简单靠谱.
排到队的, 每一个队列的后台用人工一对一服务都处理的过来…
1九、我认为铁道部的必定都知道上文的全部东西,由于我这样通常的人就知道了,也都会放进去考虑,个人系统正好是跨旧系统查询与追求效能,与铁道部需求很像,固然流量差不少。
我想主要的问题应该是当初估计尖峰流量是一亿,最后来了十亿,第二是后端系统承受不住,或是交换资料的部份承受不住,由于后端系统是内网旧系统,新旧系统 沟通还要兼顾效能会有极大挑战,就算通常采用异步MQ等方式,可是量那么大可能也是吃不完,总不能下单后十小时才给结果。
不过这篇文章很值得想开发大系统的人去思考一下,我想仍是要通过这些问题的洗礼后天然就会了,我就是这样系统搞挂几回, PM下台几个就会了,反正怎么换也不会换掉技术人员。铁道部的人下次出来应该也都很强了,没有通过这些问题真的死过的,也没人会要你去设计这类的系统。
那么多人用hibernate的甚至EJB,这样的系统这类O/RM的个人经验是挡不住的,仍是直接弄memcache/Coherence, 資料庫用 partition table等比较稳
20、我看到的几点以下:
第一,从访问结果来看12306.cn已经使用了CDN网络,提供商名字忘记了,能够查出来
第二,从访问动态页面的结果看,12306.cn用的应该是ngnix+Jboss
第三,12306.cn和奥运订票系统同样由太极集团制做
第四,从访问结果看CDN基本上没出什么问题,每次500都是由Jboss返回的。我的理解,是jboss挂了
2一、如今最大的瓶颈是 提交订单很是慢,很是困难,其次是支付接口常常出问题;登陆和查询不是那么紧。
提交订单瓶颈的缘由我估计是,铁路内部采用了网络支付限制,大约是1万张(忘记从哪里看到的),可是可能同时就是n多人同时抢票,这样,客观形成了一个排队效应;排在后面的人还要等前面的人去支付,再去更新数据库,把事务加到里面了,那是至关慢。
解决问题的首要一步是肯定这个系统达到什么效果,铁道部定时发票的节奏是不可能改变的,那套数据库也是不可能改变的,支付银行你也是没办法去改变的。僧多 粥少,刷票也是不可避免的,你也不可能作到人人上去就能买到票。这个系统建设的目的就是,用户能快速登陆,查询,若是有票可以很快买到,或者很快被告知不 被买到,并且可以避免黄牛。那么,这个系统就是成功的。
反过来看看,如今的网站呢?用户没法感知是否成功,明明看了有票就是提交不上,提交上了半天不响应,而后说不让你买了,好不容易买了交了钱了,支付那边又超时了。
因此说,我对12306的想法是:
1.找个好的产品经理,像用户看到票有而后就是提交不上去,一点其余可供感知的东西都没有,这种设计水平我就不说了;
2.优化网络流程,例如能够把网络支付作到网站里,或者彻底是异步支付;网络放票,能够作成按片不定时放,上午放上海,下午放杭州,随机放票,避免形成过多的并发;
3.再就是作好负载均衡,共享数据信息之类。其实我以为查询之类的能够用阿里云之类的来搞定啊,春运期间,搞定核心数据流程就OK了。
2二、这东西,你让百度、腾讯、阿里巴巴的团队去作都很差使。如今那个订票的web站点当然有不少问题。可是他们的开发人员确定接触不到铁道部的票务核心网,铁道部最多给他们几个调用接口,至于接口的响应速度等web站点的架构根本没法涉及。
这很是相似淘宝在双11大促销的时候,淘宝的技术很是好,浏览、查询、交易、订单,从购物到支付宝到阿里旺旺,没有任何问题,可是网银页面挂了,由于各大银行的接口跟不上。
如今就算作一个很是牛逼容许十三亿人同时并发的web站点,铁道部的票务核心系统跟不上,你这个web作再好,也只是个架子。最多就是报错提示能更人性化一点。
想要让订票变的顺畅,只有重构铁道部的票务核心系统,那估计就是一个超大的大工程。
——————如今显然是web没作好,后一步卡不卡就不知道了,由于瓶颈在web了
2三、你们都开始把系统的优化改造方案向着真实世界的购票大厅排队体系思考了,这很好,但真实世界里必定就会带来新的人为因素,例如银行的排队,就区分 我的用户、企业用户、VIP用户,高级会员啥的, 只要有人敢把12306改为排队的购票系统,就必定会弄出来不一样等级的用户权限,到时候排队的优先级、插队等真实世界的需求就都来了,那时候看你们还 说哪一个公平来着。
2四、排队算法的原型是火车站售票厅的排队买票。
而网上购票的排队,有两种方案选择,一是用户先选好从哪里到哪里的票,再排队。二是先排队,排到你了再选要从哪到哪里。
第一种,你能够想象下这种情景。售票厅为了加快速度,要求全部人排队前先领个小票,上面写着从哪到哪,若是排到你的时候没你写的票了,就不能买 了,你要回去从新领个小票重选车次和首末站,而后必然得回到队尾重排,不回到队尾的话不然你选站会妨碍后面排队的人,致使领小票再排队失去意义。
那买票的人能不造反吗?他选票时有票的,排到他时没了,得一次次重选重排,还不如如今售票厅的卖法有效率呢!
第二种,先排,排到再选票,好了,售票厅有熟练的售票员引导你,你两三分钟能买好,而网站,你给多少时间选票呢?一个旅客要先搜出想要的票的即时信息,可能没有,再搜下一趟,再搜转车方案。注意提早搜好是没多大意义的,由于票信息很快过期,只有排到你时搜出来的才有现实意义,只能排到再搜。那一我的也得给他三分钟以上来选好票下单,甚至由于不少人不熟悉操做,得给更长时间。
若是这队伍前进得比火车售票厅还慢,而网上排队的人又必然比售票厅还多,那这队你排一天都未必轮获得你,还不可能每一个人都坐在电脑前死等,网站排队变成了笑柄。
2五、我虽然也喷一次12306, 其是由于填了几个小时的验证码, 有点火,
可是内心仍是佩服的, 至少如此高的页面访问量 , 访问人数量, 网站而后还流畅的运行, 已经很不错了, (即便是说用户过多, 那只是后面系统处理不过来的缘由)
由于人太多, 拼命加硬件也能够解决,前面查询与后面支付问题, 可是只是每一年春节才用一次, 不值当的, 因此我有一个小小的想法来解决这个问题,
我说的这个问题特指: 目前网站前台接待工做作的很好, 可是把你带到后堂, 就没有人招呼了, 就跟我大学的时候, 吃饭的时间你们都拿水瓶去打水, 结果茶房挤破头, 可是呢最终都有水打, 只是要耗太多的时间在那等, 我就想你们都茶壶放在那, 而后有一我的专门来打水 , 你们都估计(根据送来水壶的时间)水打好了, 就直接到拿走就好了, 节省时间
因此我想到的方法是: 采用预订机制, (归根结底是由于没有排除)
1. 先查询出我要订哪些票(票的价格都有), 此时直接能够支付(注册时已经认证过身份证信息), 支付好了之后, 你们就该干吗干吗,
2. 系统根据预订队列, 按照系统所能使出最大能力, 生成车票, 若是可能能够短信提醒或邮件提醒订票成功与否, (若是有客户选择的时间范围,自动滚动到下一批的队列)
3. 最终仍是没有票, 执行退款动做
我想这样作的好处, 你们不用无谓的去查询多张, 再订啊订啊, 增长无谓的服务器和带宽负担, 这样就能够把前台接客的资源节省(甚至支付资源)一大部分, 并用与支付环节
2六、不少事情并非技术能够解决的。
今天是由于网上购票系统扛不住了致使买票困难,致使骂声一片,而若是真的是系统很牛叉能够抵挡的了这样的访问,那么新的问题又来了:在放票的一瞬间票就被秒杀光了。
结果会怎样?
全国人民再次抱怨买不到票,甚至怀疑购票系统。
而且这样会致使专门有人开发刷票软件。那样,再牛X的系统、再好的排队系统也无济于事的。。。由于网上处处充斥着刷票程序。这样,真正经过人工登陆去购票的人仍是买不了票。
我却是想到了一个非技术的可行办法
简单的说就是把预售改为预定,任何人均可以实名制预定某个班次火车,而后截止预定时间后系统对旅客进行虚拟购票,第一批为分组1,虚拟售罄后再从1车箱1 号座位开始虚拟售票,为第2组,再售罄后就是第三组,以此类推。假如总共虚拟售出了5组,那么在经过一个公开的方式选出某一组为得到该车次的购票权(好比当天沪深指数之和对总组数的余数+1即为该组得到购买权),而后在规定的天数内付款买票便可。
这样,即不须要那么牛叉的并不是系统,买票的人也不须要到3点去秒杀火车票了。
2七、关于老系统的问题:
2八、系统在内存里开一个大数组(32G 内存够排上亿人了吧),就是一循环队列。把这个 ticket id 放在队列尾。
======================================================================
黑传说见解:
原文已经被我改造过,为方便理解,添加了注解(斜体字),用通俗的词汇替换了一些名词(通常会在括号里加注专用名词),并对一些说法作了通俗化的更改。
说到底三个问题:
一、火车自己的运力问题:这是根本的问题,其余都是辅助
二、网站硬件上可能须要继续投入,毕竟这是一我的口大国的高交互量、须要高稳健度的网站
三、业务模式能够稍做调整,这样不只改善了用户体验,并且还节约了系统资源开销。好比排队,能够排到若干人再完成总交易,这样能够控制在本身的处理范围内。
转载 http://coolshell.cn/articles/6470.html
最简单的方式就是新系统分配到必定配额的车票,独立运行。其实这应该是最佳的方案,不然在正常状况下,网络的出票效率确定是高于其余方式,若是不作配额,99%的票都会被网络拿走。
稍微复杂一点的就是加一个仲裁的服务器,每几分钟分别向老系统和新系统放票,新老系统也是独立运行。
最后,比较笨的方法新系统经过外挂的形式和老系统交互,系统串行化请求,加个流量控制以后向老系统请票,这样新系统出票速度不会很快,但至少不会让用户卡在用户交互界面上。