天天看点

谈谈网站性能技术

一、前端负载均衡

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

二、减少前端链接数

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

1、将图片,尤其是大图片放到另外一个域名中。大站可以用另一个服务器来专门用于图片的传输。对于虚拟主机可以绑定另外一个域名。这在大网站中很常见。 图片服务器分离,且使用轻量级服务器 

2、合并css和js文件。以及在可能的时候合并图片。http://www.csssprites.com/ 这是个工具网站,它可以自动将你上传的图片合并并给出对应的background-position坐标。并将结果以png和gif的格式输出。 

谈谈网站性能技术

3、预加载,懒加载技术。最近很常见的,就是先用js显示缩略图,然后settimeout延时加载大图片。

三、基于减少体积的优化

减少体积意味着减少带宽,能够很直接地减少硬件与带宽的开支。由于网站每日接待成千上万的访客,就是一个字节的优化也可以带来显著的效果。

1、减小css、js和html的体积。

2、减小图片的体积

在ps里“储存于web和设备所用格式”可以减少不少体积。对于图片质量要求不高的图片,可以储存为gif格式。此外在gif格式下,只要增加一点点损耗就可以带来明显的体积变化。如果用“储存”菜单来保存用于web的图片,最好选择索引模式,可以至少减少三分之一的体积。

四、基于运算速度的优化

运算速度更多的是web后端的事情,但是并不意味着脱得了前端的干系。

五、前端页面静态化

静态化一些不觉变的页面和数据,并gzip一下。还有一个并态的方法是把这些静态页面放在/dev/shm下,这个目录就是内存,直接从内存中把文件读出来返回,这样可以减少昂贵的磁盘i/o。

六、优化查询

六、缓存的问题

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

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,有的就简单地轮流分析。这些都不够好,一个是不能完美地负载均衡,另一个静态的方法的致命缺陷是,如果有一台计算服务器死机了,或是我们需要加入新的服务器,对于我们的分配器来说,都需要知道。

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

五、异步、 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,就等着被骂吧)。

2、 角色分开,各司其职(web服务器,缓存服务器,负载平衡,数据库等)

5、 数据库集群和库表散列

9、常用方式。前层的反向代理加速—web  server—cache  server—db 。而负载均衡的方式也使用了如dns轮循或部分硬件负载均衡设备来承担大流量.(diy也未尝不可,一台1u的ia32硬件设备,  板子需要是为fw,switch设计的,  使用freebsd?作系统,  主要是fs和slab的改写会是技术活)。做web  加速和代理的软件如lighttpd,  nginx,haproxy .(可以不用考虑了,配置不好,不能很好的表现)压缩工具bmdiff  和  zippy(google就用了这两种技术,主要用于压缩网页数据,压缩比达到10:1——估计是存档数据,不是运算数据)

10、 选择isp注意其地区带宽分布是否平衡一致;区域dns是否优化等

11、 其他:

a) 缓存,包括cnd还是比较昂贵的,在确定缓存时要根据不同内容的访问频度进行划分,特别是注意观察缓存的命中率,如果不能达到50%以上就有问题了

b)log是所有性能调试的起点,从development  log到production  log,从rails  log到web  server  log,不同的log有不同的侧重点,学会分析log,每个log都可能为你提供解决问题的蛛丝马迹。另外,要熟练使用benchmark结合log做 profiling,当real  time远大于db  time  +  rendering  time的时候,这点尤其重要。

第三,禁止外部的盗链。

外部网站的图片或者文件盗链往往会带来大量的负载压力,因此应该严格限制外部对于自身的图片或者文件盗链,好在目前可以简单地通过refer来控制盗链,apache自己就可以通过配置来禁止盗链,iis也有一些第三方的isapi可以实现同样的功能。当然,伪造refer也可以通过代码来实现盗链,不过目前蓄意伪造refer盗链的还不多,可以先不去考虑,或者使用非技术手段来解决,比如在图片上增加水印。

第四,控制大文件的下载。

大文件的下载会占用很大的流量,并且对于非scsi硬盘来说,大量文件下载会消耗cpu,使得网站响应能力下降。因此,尽量不要提供超过2m的大文件下载,如果需要提供,建议将大文件放在另外一台服务器上。目前有不少免费的web2.0网站提供图片分享和文件分享功能,因此可以尽量将图片和文件上传到这些分享网站。

第六,使用流量分析统计软件。

在网站上安装一个流量分析统计软件,可以即时知道哪些地方耗费了大量流量,哪些页面需要再进行优化,因此,解决流量问题还需要进行精确的统计分析才可以。我推荐使用的流量分析统计软件是googleanalytics(google分析)。我使用过程中感觉其效果非常不错,稍后我将详细介绍一下 googleanalytics的一些使用常识和技巧。

先不要考虑这种问题,按照业务需求进行设计。完成后进行性能测试,然后再确定是否优化数据库,要根据性能测试的结果制定优化方案。

你现在就在这里想如果多少多少人,该如何如何的话,完全是凭空臆想而已。可能实际情况完全相反。

单表是王道

尽量少字段,大字段都迁移到别的表

不要用外键约束

尽量不使用事务

适当使用nosql

处理好读写性能的取舍 

这种项目基本都是从上之下的,迭代优化,但是如果有高并发和大用户量的人都会在架构设计,库表设计的时候会知道以后哪里会出现问题,个人不是很同意前几位说的,先跑着,然后在重构,大的门户等等都是要求4个9,须知web2.0的网站,论坛是不会给你十来八天的关机重构的,从整个架构设计上而言,无非是 cap三角,支持纵横扩展,缓存设计,负载均衡,冷备热备,读写分离,nosql键值存储,而数据库设计的话,用户量少的时候可以考虑先单机,用户量上去以后加缓存和读写分离,或者cluster,在具体的设计过程中间,划分数据实时级别,什么数据可以长时间缓存,什么数据访问量多,查少改多,什么数据应该做索引,做水平分区,按字段取模分表,什么业务和业务之间可以垂直分区(暂时可考虑在同一机器上)

另外,在数据访问层分层结构清晰,以便后续优化,改动甚至替换。不能什么的说先上线,再优化,能考虑到的地方则先考虑,比如qq,qq号,昵称,等级,取得多,而详细资料,联系方式等等去的少,这个时候在设计表的时候不能往往按照一般方式,直接设计名为user的表通通放入,则可以考虑将前者资料和后者资料分开存放,并且后者资料也可以存入qq号,昵称等等进行反范式设计,因为qq号不会变,昵称变动频率也是非常少。这也是为什么很多情况下你在某些场合依然看到的是你n年以前的昵称信息。事实上,qq的资料并非关系型数据库存储,至于架构,就是那么几招,但是具体应用和构建一些公共组件,那是做大,做好以后逐步打造的过程,这些倒是在优化和前进的过程中间不断演变。

ps:另外,互联网业务和企业级业务设计,架构方面还是有很大的差别,最好能够拿到一些案例进行一些基本的分析(其实大家的遵循的理论都是大同小异的),访问从浏览器端到数据库端点也是通过负载,长连接保持,池,缓存等等进行逐步减压和过载保护(防止雪崩),以达到请求递减。

其实数据库可以考虑按照dz的数据库设计,具体存储方面可以考虑使用文件存储,减轻服务器压力,也就是key-value系统。

用户相关表可以考虑进行分表操作。

然后定时作业,将过往帖进行静态化处理,如一个周没有新动态的帖子静态化处理。

基本上就能够满足你的需求了。不要考虑太复杂了,有的时候静态化是最简单的方式。

然后将数据库中的数据尽可能多的放到内存中缓存起来也是一个好方法。

另外就是需要考虑集群的性能问题了。提高系统的横向扩展能力