所谓系统的伸缩性是指不须要改变系统的软硬件设计,仅仅经过改变部署的服务器数量就能够扩大或者缩小系统的服务处理能力。算法
在应用系统渐进式的演化过程当中,最重要的技术手段就是使用服务器集群,经过不断地向集群中添加服务器来加强整个集群的处理能力。数据库
通常说来,系统的伸缩性设计可分红两类,一类是根据功能进行物理分离实现伸缩,一类是单一功能经过集群实现伸缩。前者是不一样的服务器部署不一样的服务,提供不一样的功能;后者是集群内的多台服务器部署相同的服务,提供相同的功能。segmentfault
在应用系统发展的早期,经过增长服务器提升处理能力时,新增服务器老是从现有服务器中分离出部分功能和服务。具体又能够分红纵向分离和横向分离两种状况,纵向分离也即业务分层,横向分离则是业务拆分。浏览器
将不一样功能分离部署能够实现必定程度的伸缩性,可是随着访问量的逐步增长,即便分离到最小粒度,单一的服务器也不能知足业务规模的要求。所以必须使用服务器集群,即将相同服务部署在多台服务器上构成一个集群总体对外提供服务。缓存
应用服务器应该设计成无状态的,若是将部署有相同应用的服务器组成一个集群,每次用户请求均可以发送到集群中任意一台服务器上去处理。这样只要能将用户请求按照某种规则分发到集群的不一样服务器上,就能够构成一个应用服务器集群。服务器
这个请求分发装置就是所谓的负载均衡器。负载均衡是网站必不可少的基础技术手段,不但能够改善网站的可用性,同时还能够实现网站的伸缩性。具体的技术实现也多种多样,基础技术不外如下几种。网络
HTTP重定向服务器是一台普通的应用服务器,其惟一的功能就是根据用户的HTTP请求计算一台真实的Web服务器地址,并将该Web服务器地址写入HTTP重定向响应中(响应状态码302)返回给用户浏览器。数据结构
这种负载均衡方案的优势是比较简单。缺点是浏览器须要两次请求服务器才能完成一次访问,性能较差;重定向服务器自身的处理能力有可能成为瓶颈,整个集群的伸缩性规模有限;使用302响应码重定向,有可能使搜索引擎判断为SEO做弊,下降搜索排名。所以这种方案不多见。架构
咱们能够在DNS服务器中为应用网址注册多条A记录,这样的话,每次域名解析请求都会根据负载均衡算法计算一个不一样的IP地址返回,A记录中配置的多个服务器就构成一个集群,实现了负载均衡。负载均衡
DNS域名解析负载均衡的优势是将负载均衡的工做转交给DNS,省掉了网站管理维护负载均衡服务器的麻烦,同时许多DNS还支持基于地理位置的域名解析,即会将域名解析成距离用户地理最近的一个服务器地址,这样可加快用户访问速度。
可是DNS域名解析负载均衡也有缺点,DNS是多级解析,每一级DNS均可能缓存A记录,当下线某台服务器后,即便修改了DNS的A记录,要使其生效也须要较长时间,这段时间,DNS依然会将域名解析到已经下线的服务器,致使用户访问失败;并且DNS负载均衡的控制权在域名服务商那里,网站没法对其作更多改善和更强大的管理。
大型网站老是部分使用DNS域名解析,利用域名解析做为第一级负载均衡手段。域名解析获得的一组服务器并非实际提供Web服务的物理服务器,而是一样提供负载均衡服务的内部服务器,这组内部负载均衡服务器再进行负载均衡,将请求分发到真实的Web服务器上。
以前咱们提到能够利用反向代理缓存资源,以改善网站性能。同时大多数反向代理服务器还提供负载均衡的功能,管理一组Web服务器,将请求根据负载均衡算法转发到不一样Web服务器上,Web服务器处理完成的响应也须要经过反向代理服务器返回给用户。
因为反向代理服务器转发请求在HTTP协议层面,所以也叫应用层负载均衡。其优势是和反向代理服务器功能集成在一块儿,部署简单。缺点是反向代理服务器是全部请求和响应的中转站,其性能可能会成为瓶颈。
在网络层,能够经过修改请求目标地址进行负载均衡。用户请求数据包到达负载均衡服务器后,负载均衡服务器根据负载均衡算法计算获得一台真实Web服务器地址,而后将数据包的目的IP地址修改成该地址。真实Web应用服务器处理完成后,响应数据包回到负载均衡服务器,负载均衡服务器再将数据包源地址修改成自身的IP地址发送给用户浏览器。
这里的关键在于真实物理Web服务器响应数据包如何返回给负载均衡服务器。一种方案是负载均衡服务器在修改目的IP地址的同时修改源地址,将数据包源地址设为自身IP,即源地址转换(SNAT),这样Web服务器的响应会再回到负载均衡服务器;另外一种方案是将负载均衡服务器同时做为真实物理服务器集群的网关服务器,这样全部响应数据都会到达负载均衡服务器。
IP负载均衡在内核进程完成数据分发,较反向代理负载均衡(在应用程序中分发数据)有更好的处理性能。可是因为全部请求响应都须要通过负载均衡服务器,集群的最大响应数据吞吐量不得不受制于负载均衡服务器网卡带宽。
那么,能不能让负载均衡服务器只分发请求,而使响应数据从真实物理服务器直接返回给用户呢?
顾名思义,数据链路层负载均衡是指在通讯协议的数据链路层修改mac地址进行负载均衡。负载均衡数据分发过程当中不修改IP地址,只修改目的mac地址,经过配置真实物理服务器集群全部机器虚拟IP和负载均衡服务器IP地址一致,从而达到不修改数据包的源IP地址和目的IP地址就能够进行数据分发的目的。
因为实际处理请求的真实物理服务器IP和数据请求目的IP一致,不须要经过负载均衡服务器进行地址转换,可将响应数据包直接返回给用户浏览器,避免负载均衡服务器网卡带宽成为瓶颈。这种负载均衡方式又称做直接路由方式(DR)。
如图所示,用户请求到达负载均衡服务器114.100.80.10后,负载均衡服务器将请求数据的目的mac地址修改成00:0c:29:d2,因为Web服务器集群全部服务器的虚拟IP地址都和负载均服务器的IP地址相同,所以数据能够正常传输到达mac地址00:0c:29:d2对应的服务器,该服务器处理完成后发送响应数据到网站的网关服务器,网关服务器直接将该数据包发送到用户浏览器,响应数据不须要经过负载均衡服务器。
数据链路层负载均衡是目前使用较广的一种负载均衡手段。在Linux平台上最好的链路层负载均衡开源产品是LVS(Linux Virtual Server)。
前面描述了如何将请求数据发送到Web服务器,而具体的负载均衡算法一般有如下几种:
和全部服务器都部署相同应用的应用服务器集群不一样,分布式缓存服务器集群中不一样服务器中缓存的数据各不相同,缓存访问请求不能够在缓存服务器集群中的任意一台处理,必须先找到缓存有须要数据的服务器,而后才能访问。这个特色会严重制约分布式缓存集群的伸缩性设计,由于新上线的缓存服务器没有缓存任何数据,而已下线的缓存服务器还缓存着网站的许多热点数据。
必须让新上线的缓存服务器对整个分布式缓存集群影响最小,也就是说新加入缓存服务器后应使整个缓存服务器集群中已经缓存的数据尽量还被访问到,这是分布式缓存集群伸缩性设计的最主要目标。
在分布式缓存服务器集群中,对于服务器集群的管理,路由算法相当重要,和负载均衡算法同样,决定着究竟该访问集群中的哪台服务器。
余数Hash是最简单的一种路由算法:用服务器数目除以缓存数据KEY的Hash值,余数为服务器列表下标编号。因为HashCode具备随机性,所以使用余数Hash路由算法可保证缓存数据在整个服务器集群中比较均衡地分布。
对余数Hash路由算法稍加改进,就能够实现和负载均衡算法中加权负载均衡同样的加权路由。事实上,若是不须要考虑缓存服务器集群伸缩性,余数Hash几乎能够知足绝大多数的缓存路由需求。
可是,当分布式缓存集群须要扩容的时候,会出现严重的问题。很容易就能够计算出,若是由3台服务器扩容至4台服务器,大约有75%(3/4)被缓存了的数据不能正确命中,随着服务器集群规模的增大,这个比例线性上升。当100台服务器的集群中加入一台新服务器,不能命中的几率是99%(N/(N+1))。
一种解决办法是在网站访问量最少的时候扩容缓存服务器集群,这时候对数据库的负载冲击最小。而后经过模拟请求的方法逐渐预热缓存,使缓存服务器中的数据从新分布。可是这种方案对业务场景有要求,还须要选择特定的时段,要求较为严苛。
一致性Hash算法经过一个叫做一致性Hash环的数据结构实现KEY到缓存服务器的Hash映射,如图所示。
具体算法过程为:先构造一个长度为0~2³²的整数环(这个环被称做一致性Hash环),根据节点名称的Hash值(其分布范围一样为0~2³²)将缓存服务器节点放置在这个Hash环上。而后根据须要缓存的数据的KEY值计算获得其Hash值(其分布范围也一样为0~2³²),而后在Hash环上顺时针查找距离这个KEY的Hash值最近的缓存服务器节点,完成KEY到服务器的Hash映射查找。
当缓存服务器集群须要扩容的时候,只须要将新加入的节点名称(NODE3)的Hash值放入一致性Hash环中,因为KEY是顺时针查找距离其最近的节点,所以新加入的节点只影响整个环中的一小段,如图6中深色一段。
加入新节点NODE3后,原来的KEY大部分还能继续计算到原来的节点,只有KEY三、KEY0从原来的NODE1从新计算到NODE3。这样就能保证大部分被缓存的数据还能够继续命中。
具体应用中,这个长度为2³²的一致性Hash环一般使用二叉查找树实现,Hash查找过程其实是在二叉查找树中查找不小于查找数的最小数值。固然这个二叉树的最右边叶子节点和最左边的叶子节点相链接,构成环。
可是,上述算法还存在一个小小的问题。假设本来3台服务器的负载大体相等,新加入的节点NODE3只分担了节点NODE1的部分负载,这就意味着NODE0和NODE2缓存数据量和负载压力比NODE1与NODE3的大,从几率上来讲大约是2倍。这种结果显然不是咱们想要的。
解决办法也很简单,计算机的任何问题均可以经过增长一个虚拟层来解决。解决上述一致性Hash算法带来的负载不均衡问题,也能够经过使用虚拟层的手段:将每台物理缓存服务器虚拟为一组虚拟缓存服务器,将虚拟服务器的Hash值放置在Hash环上,KEY在环上先找到虚拟服务器节点,再获得物理服务器的信息。
这样新加入物理服务器节点时,是将一组虚拟节点加入环中,若是虚拟节点的数目足够多,这组虚拟节点将会影响一样多数目的已经在环上存在的虚拟节点,这些已经存在的虚拟节点又对应不一样的物理节点。最终的结果是:新加入一台缓存服务器,将会较为均匀地影响原来集群中已经存在的全部服务器。如图所示。
显然每一个物理节点对应的虚拟节点越多,各个物理节点之间的负载越均衡,新加入物理服务器对原有的物理服务器的影响越保持一致,可是太多又会影响性能。那么在实践中,一台物理服务器虚拟为多少个虚拟服务器节点合适呢?通常说来,经验值是150,固然根据集群规模和负载均衡的精度需求,这个值应该根据具体状况具体对待。
数据存储服务器集群的伸缩性对数据的持久性和可用性提出了更高的要求,由于数据存储服务器在任何状况下都必须保证数据的可用性和正确性。
目前,市面上主要的关系数据都支持数据复制功能,使用这个功能能够对数据库进行简单伸缩。下图为使用数据复制的MySQL集群伸缩性方案。
在这种架构中,多台MySQL实例有主从之分,数据写操做都在主服务器上,由主服务器将数据同步到集群中其余从服务器,数据读操做及数据分析等离线操做在从服务器上进行。
除了数据库主从读写分离,前面提到的业务分割模式也能够用在数据库,不一样业务数据表部署在不一样的数据库集群上,即俗称的数据分库。
在大型系统中,即便进行了分库和主从复制,对一些单表数据仍然很大的表,还须要进行分片,将一张表拆开分别存储在多个数据库中。目前市场上经常使用的分库分表的中间件是Mycat。
相比关系数据库自己功能的强大,目前各种分库分表中间件的功能都显得很是简陋,限制了关系数据库某些功能的使用。可是当网站业务面临不停增加的海量业务数据存储压力时,又不得不利用分布式关系数据库的集群伸缩能力,这时就必须从业务上回避分布式关系数据库的各类缺点:避免事务或利用事务补偿机制代替数据库事务;分解数据访问逻辑避免JOIN操做等。