前言
通过垂直伸缩和水平伸缩方式构建可伸缩系统。
一致性hash
在GFS中,对于上面问题采取的解决方法是增加一个称为主服务器的单点机器。当Node A要上传文件时,Node A上的GFS Client会将文件按固定大小划分,并向主服务器提交文件名和块索引信息,从而得到要存储的目标机器及位置,主服务器根据目前各存储机器的存活状态、硬件使用率等来决定块需要存储到的目标机器,之后Node A将数据存储到目标机器的相应位置上。
主服务器负责记录文件和块的命名空间、文件到块的映射及每个块副本的位置。为了保证安全可靠,同时将数据复制到多个存储机器上,复制的份数可在主服务器上进行设置,当Node B要读取此文件时,则只要从主服务器上获取此文件划分的存储位置列表,然后随机挑选机器进行读取,最后根据块的索引进行合并即可。
主服务器与各个存储机器进行心跳,以保证存储机器是有效的。Node A这类应用端为了避免每次都要和主服务器进行通信,可在一定时间内缓存文件的元数据信息。GFS在存储时将文件分割为固定的块,块的大小默认情况下固定为64MB。对于大文件,划分为多块存储在不同的服务器上,读写时都是同时操作多台服务器,因此速度比较快。但对于小文件而言,则意味每次只能从一台服务器上读取,当存取大量小文件时,会对向主服务器的查询造成一定的压力。因此GFS并不适用于存储大量小文件的系统,GFS之所以将块设置为64MB的原因是Google的业务场景中大部分都是大文件,另外对于主服务器而言,块越大,要存储的信息就越少,这样可以降低主服务器的内存消耗和压力。由于GFS这类分布式文件系统只须采用大量廉价的机器就可构成一个巨大的存储空间,并且具备了很好的水平伸缩能力,因此目前多数互联 公司都选择采用分布式文件系统来存储海量文件。
应用的水平伸缩方法
在系统建设初期,会采用将各种业务都放在同一个系统的方式,这会导致这个系统日渐庞大,所需的资源(CPU、内存、数据库连接)越来越多,在进行水平伸缩时要考虑系统里各种业务会造成的资源增加的现象,这种状况会导致水平伸缩很难进行。
例如增加机器后就造成了多个数据库连接的增加,对于这样的状况,通常采取拆分应用的方式来解决。拆分应用通常按照业务领域来划分,即将原在同一系统中处理的功能拆分到各个不同的业务系统中,例如eBay将其业务系统拆分为商品、用户、评价、交易等,拆分后的结构如图:
- 页面静态化对于一些信息变动不多,且无须根据访问用户来进行展示的页面而言,可转为生成静态的页面,这样当用户访问这些页面时,就无须再从数据库中读取了,一方面可提升系统的响应速度;另一方面可降低对后端的访问压力,从而降低数据库连接数,例如新闻页面或 站的通告页面等可如此。
- 页面片段缓存对于一些不能静态化的页面,页面中仍然会有些部分相对变化不大,且无须根据访问用户来展示的片段。对于这样的页面,可将这些片段信息进行缓存,当用户访问时,Web服务器只需从缓存中获取这些片段信息即可,而无须对数据库进行访问,通常可基于ESI[插图]等方式实现。
- 数据缓存对于一个系统而言,大部分的功能仍须与数据库交互才可完成,对于这些需要访问数据库的功能,可通过将数据缓存来提升响应速度和降低对数据库的压力。例如用户信息,需要缓存的数据量会比较大,因此通常会采用分布式缓存来实现。数据缓存仅适用于变化不多的数据信息,如变化太多,一方面无法保证缓存的高命中率,另一方面导致需要频繁地更新缓存,性能反而不如直接操作数据库好。
以上三种缓存方式对于降低数据库连接可起到明显的作用,这对于实现水平伸缩也会有一定的帮助。
分库
在系统发展的初期,通常会将各种不同的数据放在同一个数据库中,随着业务的多元化及业务系统的水平伸缩,数据库的连接数会成为稀有资源,对于这种情况,分库是个不错的选择。分库通常按照业务领域将原来存储在同一个数据库的数据拆分到多个数据库中。例如eBay就按照其业务领域拆分为商品、用户、评价、交易等数据库,每个数据库只用处理相关业务的数据,因此可用的数据库连接数就会得到很大提升。
对于系统而言,通常会带来如下几个问题:
- 对于已有的系统,这意味着要将之前访问同一个库的地方修改为访问相应的库,通常这会造成已有众多系统都要修改;
- 对于有些要跨数据库操作的业务而言,就变得较为复杂了,通常要从原来的联合查询转变为多次查询,而写入类的如要保证事务则可能要引入分布式事务。
分库是系统发展到一定规模后通常采用的手段,其对于支撑业务系统的水平伸缩可以起到很大的作用。
异步数据库访问
目前大部分数据库访问仍然采用同步方式,每进行一次数据库操作就要占用一个数据库连接,并且要等到数据库操作执行完毕才会将连接释放。这对于高并发的系统而言就很容易出现数据库连接不够用及数据库资源竞争激烈的现象,异步数据库访问是解决这种现象的一种方法。异步数据库访问要将传统的通过阻塞IO访问数据库的方式转变为采用非阻塞或异步IO的方式来访问。为了支持连接的复用,访问数据库端在执行数据库操作时要生成一个请求ID,数据库服务器在执行完毕后要将此请求ID传回访问端,在支持了连接复用后,就可做到用很少的连接来支撑大量的数据库访问及避免连接资源竞争的现象,在采用异步数据库访问后访问数据库的操作流程如图
多master
为提升数据写的速度,通常可采用的方法是建立多个master,在建立多个master时最理想的状态是多个master没有关联,也就是多个master的数据并不相同。例如按用户将其所拥有的物品划分到一个单独的master机器去写,这样当用户需要操作时只要操作所对应的master机器即可,这种仅适用于读取数据时无须联合读取的情况。
另外一种多master的状况则为每个master的数据要保持一致,此时一个明显的挑战是数据一致性的问题,另外一个明显的挑战是自增ID的问题。
数据一致性的问题通常采用复制、两阶段提交、三阶段提交或google paxos来解决,复制要求数据具备版本信息,而对于有些场景而言,必须做到同步的一致性。例如用户的存款余额,在master A数据库修改的同时,在其他的master数据库也必须同时修改成功,对于此类现象,需要借助两阶段提交、三阶段提交或google paxos来解决。
自增ID的问题通常要改为由程序来生成ID的方式解决。在解决了以上问题后,即可通过水平伸缩来提升数据库的写速度。
提升计算能力
在单机计算的情况下,即使进行垂直伸缩,PC Server的计算能力也是无法和小型机抗衡的,因此要有合理的设计来通过增加PC Server提升系统的计算能力。在垂直伸缩场景中采取的方法是将计算任务拆分,多线程并行计算然后汇总结果。在水平伸缩场景中则可演变为将计算任务拆分,然后分派给不同的机器进行计算,最后汇总。它复杂的地方在于拆分的方法、拆分后的分派调度及机器执行失败时的处理,在Java中主要可采用的方法如MapReduce,MapReduce=的方式都支持将任务拆分后分解到多台机器上执行。在计算任务不变的情况下,增加机器可使得每台机器上执行的计算任务减少,从而提高速度;倘若计算任务增加,增加机器可让每台机器需要执行的计算任务不变,从而保持系统的计算能力。
文章知识点与官方知识档案匹配,可进一步学习相关知识Java技能树首页概览92904 人正在系统学习中
声明:本站部分文章及图片源自用户投稿,如本站任何资料有侵权请您尽早请联系jinwei@zod.com.cn进行处理,非常感谢!