铁路局官网app怎么优化的
1,图像验证码替代数字验证码
2,火车票这个事,还有很多查询操作,查时间,查座位,查铺位,一个车次不 行,又查另一个车次,其伴随着大量的查询操作,下单的时候需要对数据库操作。
12306的高峰访问是10亿页面访问量,集中在早8点到10点,每秒页面访问量在高峰时上千万。
一、前端负载均衡
通过DNS的负载均衡器(一般在路由器上根据路由的负载重定向)可以把用户的访问均匀地分散在多个Web服务器上。这样可以减少Web服务器的请求 负载。因为http的请求都是短作业,所以,可以通过很简单的负载均衡器来完成这一功能。最好是有CDN网络让用户连接与其最近的服务器(CDN通常伴随 着分布式存储)。(关于负载均衡更为详细的说明见“后端的负载均衡”)
二、减少前端链接数
我看了一下12306.cn,打开主页需要建60多个HTTP连接,车票预订页面则有70多个HTTP请求,现在的浏览器都是并发请求的。所以,只 要有100万个用户,就会有6000万个链接,太多了。一个登录查询页面就好了。把js打成一个文件,把css也打成一个文件,把图标也打成一个文件,用 css分块展示。把链接数减到最低。
—— 注:以前拨号连接时代,因为带宽小,大图片没下载完,用户已经关闭了浏览器页面,所以对连接数要求不高,但对一次的下载量要求很高。但现在带宽已经足够, 一次的下载量已经不再是瓶颈,所以,不能再以过去那种方式处理这个问题,而应该合并文件,尽量减少请求数,从而减少交互频率,把节约出的交互资源用于网站 和用户的其他更有价值的互动上。
三、减少网页大小增加带宽
这个世界不是哪个公司都敢做图片服务的,因为图片太耗带宽了。现在宽带时代很难有人能体会到当拨号时代做个图页都不敢用图片的情形(现在在手机端浏 览也是这个情形)。我查看了一下12306首页的需要下载的总文件大小大约在900KB左右,如果你访问过了,浏览器会帮你缓存很多,只需下载10K左右 的文件。但是我们可以想像一个极端一点的案例,1百万用户同时访问,且都是第一次访问,每人下载量需要1M,如果需要在120秒内返回,那么就需要,1M * 1M /120 * 8 = 66Gbps的带宽。很惊人吧。所以,我估计在当天,12306的阻塞基本上应该是网络带宽,所以,你可能看到的是没有响应。后面随着浏览器的缓存帮助 12306减少很多带宽占用,于是负载一下就到了后台,后台的数据处理瓶颈一下就出来。于是你会看到很多http 500之类的错误。这说明服务器垮了。
四、前端页面静态化
静态化一些不常变的页面和数据,并压缩一下(比如gzip,当然,也要考虑压缩和读取的资源消耗问题)。还有一个变态的方法是把这些静态页面放在内存,也就是(/dev/shm目录下),直接从内存中把文件读出来返回,这样可以减少对磁盘的读写
注:因为磁盘读写相对于内存读写来说太慢。
五、优化查询
很多人查询都是在查一样的,完全可以用反向代理合并这些并发的相同的查询。这样的技术主要用查询结果缓存来实现,第一次查询走数据库获得数据,并把 数据放到缓存,后面的查询统统直接访问高速缓存。为每个查询做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这个系统有非常有意义的质的性能的提高。
四、后台系统负载均衡
前面说了数据拆分存放,数据拆分存放可以在一定程度上减轻系统负载,但是无法减轻热销商品的负载,对于火车票来说,可以认为是大城市的某些主干线上 的车票。这就需要镜像数据来减轻负载。镜像了数据,必然要用到负载均衡,在后台,我们可能很难使用像路由器上的负载均衡器,因为那是均衡流量的,因为流量 并不代表服务器的繁忙程序。因此,我们需要一个任务分配系统,其还能监控各个服务器的负载情况。
任务分配服务器有一些难点:
中央分配式:看到有很多系统都用静态的方式来分配,有的用hash,有的就简单地轮流分析。这些都不够好,一个是不能完美地负载均衡,另一个静态的方法的致命缺陷是,如果有一台计算服务器死机了,或是我们需要加入新的服务器,对于我们的分配器来说,都需要重新刷新。
下游请缨式:由下游的计算服务器去任务服务器上拿任务。让这些计算服务器自己决定自己是否要任务。这样的好处是可以简化系统的复杂度,而且还可以任意实时地减少或增加计算服务器。但是唯一不好的就是,如果有一些任务只能在某种服务器上处理,这可能会引入一些复杂度。 不过总体来说,这种方法可能是比较好的负载均衡。
五、异步处理、 限流阀 和 批量处理
异步、限流阀(throttle) 和批量处理都需要对并发请求数做队列处理的。
所以,只要是异步,一般都会有限流阀机制,一般都会有队列来排队,有队列,就会有持久化,而系统一般都会使用批量的方式来处理。
云风同学设计的“排队系统” 就是这个技术。这和电子商务的订单系统很相似,就是说,我的系统收到了你的购票下单请求,但是我还没有真正处理,我的系统会跟据我自己的处理能力来限制住这些大量的请求,并一点一点地处理。一旦处理完成,我就可以发邮件或短信告诉用户你来可以真正购票了。
在这里,我想通过业务和用户需求方面讨论一下云风同学的这个排队系统,因为其从技术上看似解决了这个问题,但是从业务和用户需求上来说可能还是有一些值得我们去深入思考的地方:
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、使用浏览器缓存
在页面加载中,12306请求了如此多的资源,很多资源看起来,根本就是不太容易变化的,应在HTTP标头中设置有效期,尽可能多的使用浏览器缓存。
优化2、图片优化
数据传输时间,在访问网站的过程中,是一个耗时比较大的过程,其中又以图片传输为最,如果网站上有较多的图片,那么就要想办法减少体积,延迟加载等等。在12306的页面上,logo(https://kyfw.12306.cn/otn/resources/images/logo.png ),icon(https://kyfw.12306.cn/otn/resources/images/logo.png )等等图片都是可以优化的。
优化3、图片组合为CSS贴图
浏览器一般都有并发连接数限制,也就是同时请求的资源数量是有效的,前端优化点之一就是减少请求数量,那么12306中的诸多小图片完全可以合并到一个大图之中,采用贴图定位的方式,降低请求数量。
优化4、暂缓JS解析
由于JS是阻塞加载的,一般来说,把js放在head中会影响页面的渲染速度,很多时候,我们都推荐把js放在body结束标记之前。但12306偏偏没有这么做,把大把的js放在head中。
优化5、使用css而不是图片控制背景
这个似乎是大家都知道的常识,就算为了兼容老版本的浏览器,也可以考虑做优雅降级。但12306偏偏就大量使用背景图。
优化6、CSS合并
同样为了减少请求数,应该尽量将CSS压缩合并。分析12306的站点css,发现部分合并了,部分没有,而且有些css连压缩都没做,很难想象是怎么打算的。另外,外部控件的样式(不会变的样式)完全可以打包放到cdn上。
优化7、JS合并
JS同上,该打包就打包,不要搞一堆js出来,加载还慢。。
秒杀系统
而秒杀,直接杀就好了。另外,关于秒杀,完全可以做成只接受前N个用户的请求 (完全不操作后端的任何数据, 仅仅只是对用户的下单操作log),这种业务,只要把各个服务器的时间精确同步了就可以了,无需在当时操作任何数据库。可以订单数够后,停止秒杀,然后批量写数据库。
数据库性能和优化
银行系统架构设计,内外系统交互如何保证安全
电子商务的订单系统
需要对库存进 行:1)占住库存,2)支付(可选),3)扣除库存的操作。这个是需要有一致性的检查的,也就是在并发时需要对数据加锁的。B2C的电商基本上都会把这个 事干成异步的,也就是说,你下的订单并不是马上处理的,而是延时处理的,只有成功处理了,系统才会给你一封确认邮件说是订单成功。我相信有很多朋友都收到 认单不成功的邮件。这就是说,数据一致性在并发下是一个瓶颈。
库存问题
集中式系统
用一句话概括就是:一个主机带多个终端。终端没有数据处理能力,仅负责数据的录入和输出。而运算、存储等全部在主机上进行。现在的银行系统,大部分都是这种集中式的系统,此外,在大型企业、科研单位、军队、政府等也有分布。集中式系统,主要流行于上个世纪。
于采用单机部署。很可能带来系统大而复杂、难于维护、发生单点故障(单个点发生故障的时候会波及到整个系统或者网络,从而导致整个系统或者网络的瘫痪)、扩展性差等问题。
分布式系统
分布性
分布式系统中的多台计算机之间在空间位置上可以随意分布,系统中的多台计算机之间没有主、从之分,即没有控制整个系统的主机,也没有受控的从机。
透明性
系统资源被所有计算机共享。每台计算机的用户不仅可以使用本机的资源,还可以使用本分布式系统中其他计算机的资源(包括CPU、文件、打印机等)。
同一性
系统中的若干台计算机可以互相协作来完成一个共同的任务,或者说一个程序可以分布在几台计算机上并行地运行。
通信性
系统中任意两台计算机都可以通过通信来交换信息。
和集中式系统相比,分布式系统的性价比更高、处理能力更强、可靠性更高、也有很好的扩展性。但是,分布式在解决了网站的高并发问题的同时也带来了一些其他问题。首先,分布式的必要条件就是网络,这可能对性能甚至服务能力造成一定的影响。其次,一个集群中的服务器数量越多,服务器宕机的概率也就越大。另外,由于服务在集群中分布是部署,用户的请求只会落到其中一台机器上,所以,一旦处理不好就很容易产生数据一致性问题。
一群独立计算机集合共同对外提供服务,但是对于系统的用户来说,就像是一台计算机在提供服务一样。分布式意味着可以采用更多的普通计算机(相对于昂贵的大型机)组成分布式集群对外提供服务。计算机越多,CPU、内存、存储资源等也就越多,能够处理的并发访问量也就越大。
JVM
JAVA虚拟机JVM是属于JRE的,而现在我们安装JDK时也附带安装了JRE(当然也可以单独安装JRE)
JVM有自己完善的硬件架构,如处理器、堆栈、寄存器等,还具有相应的指令系统。Java语言最重要的特点就是跨平台运行。使用JVM就是为了支持与操作系统无关,实现跨平台。
JVM的内部体系结构分为三部分,分别是:
类装载器(ClassLoader)子系统=>启动类装载器和用户自定义类装载器
运行时数据区=>
方法区=>当JVM的类装载器加载.class文件,并进行解析,把解析的类型信息放入方法区。
堆=>存放所有程序在运行时创建的对象
Java栈(当前线程中局部基本类型的变量(java中定义的八种基本类型:boolean、char、byte、short、int、long、float、double)、部分的返回结果以及Stack Frame,非基本类型的对象在JVM栈上仅存放一个指向堆上的地址),
PC寄存器,
本地方法栈=>存储本地方法调用的状态
执行引擎
JVM垃圾回收
GC的基本原理:将内存中不再被使用的对象进行回收,GC中用于回收的方法称为收集器,由于GC需要消耗一些资源和时间,Java在对对象的生命周期特征进行分析后,按照新生代、旧生代的方式来对对象进行收集,以尽可能的缩短GC对应用造成的暂停
(1)对新生代的对象的收集称为minor GC;
(2)对旧生代的对象的收集称为Full GC;
(3)程序中主动调用System.gc()强制执行的GC为Full GC。
不同的对象引用类型, GC会采用不同的方法进行回收,JVM对象的引用分为了四种类型:
(1)强引用:默认情况下,对象采用的均为强引用(这个对象的实例没有其他对象引用,GC时才会被回收)
(2)软引用:软引用是Java中提供的一种比较适合于缓存场景的应用(只有在内存不够用的情况下才会被GC)
(3)弱引用:在GC时一定会被GC回收
(4)虚引用:由于虚引用只是用来得知对象是否被GC