扩展的3个简单原则

举报
feichaiyu 发表于 2020/02/16 15:16:38 2020/02/16
【摘要】 本书给出了设计高扩展网站的50条原则,如不要过度设计、设计时就考虑扩展性、把方案简化3倍以上、减少DNS查找、尽可能减少对象等,每个原则都与不同的主题绑定在一起。大部分原则是面向技术的,只有少量原则解决的是与关键习惯和方法有关的问题,当然,每个原则都对构建可扩展的产品至关重要。 本书适合各层次Web开发人员阅读。

如何通过克隆和复制、拆分功能或服务以及把相似的数据集分布到存储和应用系统中,从而扩展数据库和服务。只要利用本文介绍的这三种方法,几乎任何系统或数据库都可以无限扩展。这里用方法这个词有点牵强,但是根据我们为上百家公司、几千个数据库和系统服务的经验来看,这些技术还没有失败过。为了使这三种扩展方法更形象,我们采用AKF扩展立方,它是我们为了展示这三种扩展系统的方法而设计的。图1就是AKF扩展立方,是以我们的合作伙伴AKF Partners命名的。


1581837274647659.png

图1 AKF扩展立方

AKF扩展立方的核心是三条轴,每条轴都代表一个关于扩展的原则。这个立方很好地表示出了从0扩展性(立方体的前端左下角)到接近无穷大扩展性(立方体的后端右上角)的过程。有时,只看三条轴而不看其中的空间,会更容易理解一些。图2展示了三条轴和与之相关的原则。我们将在本文中逐一介绍这些原则。

1581837304287612.png


图2 扩展的三条轴

原则1:横向复制(X轴原则)

目的:横向扩展,即复制服务或数据库来分散事务负载。

适用情形:

  • 具有非常高读写比例(5∶1或更高,越高越好)的数据库;

  • 事务增长大于数据增长的系统。

应用方式:

  • 只需克隆服务并实施负载均衡;

  • 对于数据库,要确保访问代码能够区分读写操作。

应用理由:复制数据和功能可以使事务更快地扩展。

要点:X轴拆分方法能够快速实现,但是只能提高事务的扩展性,不能提高数据的扩展性。

系统最难扩展的部分通常是数据库或者持久存储层。该问题可以追溯到Edgar F. Codd于1970年发表的论文A Relational Model of Date for Large Shared Data Banks,该论文被认为首次引入了关系数据库管理系统(RDBMS)的概念。当今最流行的RDBMS,如Oracle、MySQL和SQL Server等,如其名字所示,都用于管理数据元素之间的关系。这些关系可以存在于表内,也可以存在于表之间。大多数联机事务处理(OLTP)系统中的表都被规范化为第三范式,即表中的所有记录都有相同的字段,所有非关键字段都不能只依赖于组合关键字的一部分,所有非关键字段都必须依赖于关键字。表中的每一列数据与其他列数据是有关系的。表之间的关系,通常称为外键。大多数使用数据库的应用都有赖于数据库基于其ACID属性(参阅表1)支持并实施这些关系。维护和实施这些关系使得拆分数据库需要很多工作。

表1 数据库的ACID属性

属  性

说  明

原子性(Atomicity)

要么完成事务中的所有操作,要么一个都不执行

一致性(Consistency)

事务开始和结束时,数据库的所有数据要保持状态一致

隔离性(Isolation)

事务的表现就像它是对数据库执行的唯一操作

持久性(Durabitity)

事务完成时,操作将不能更改

扩展数据库的技术之一是利用大多数应用和数据库执行的读操作比写操作多这一事实。我们的一个客户负责为顾客预定酒店,每次预定平均需要检索400次。每个预定都是1次写操作,而每次检索则是1次读操作,这样就导致了读写比例为400∶1。创建数据的只读副本就可以轻松地扩展这种类型的系统。

根据数据的时间敏感度,有两种方法可以分布数据的只读副本。所谓时间敏感度,指的是相对于数据的写副本来说,只读副本有多么新,或者是否完全正确。在你坚持要求整个系统的数据是即时、同步且完全正确之前,仔细考虑一下这种系统的成本有多高吧。虽然完全同步数据是理想状态,但它的成本真的很高。况且,这种情况的性价比可能也并不是你想要的。

让我们再看看那个每写1次就需要400次读操作的预定系统吧。它处理的是顾客的预定,所以你可能认为他们要显示给顾客的是完全同步的数据。首先,要给顾客提供的一条预定数据必须保持400个数据集同步。其次,数据与主事务数据库之间有3秒、30秒或者90秒的不同步,并不意味着该数据一定是错的,只是存在这种几率。该客户的系统中可能一直保存着10万条数据,每天预定的有10%。如果这些预定平均分布在一天中,那么大约一秒(0.86秒)完成一次预定。在机会均等的情况下,一位顾客想预定另一位顾客刚定的房间的可能性是0.104%(假设数据每90秒同步一次)。当然,顾客还有0.1%的可能性选择已经预定过的房间,虽然这不太理想,但在顾客把预定的房间加入购物车之前再做一次最后检查就可以避免这种情况。当然,每个应用的数据需求都不同,但从我们的讨论中,希望你能明白应该如何抵制所有数据必须实时同步的想法。

讨论过时间敏感度了,那么让我们来看看分布数据的方法。一种方法是在数据库前端使用缓存层。每次查询可以读取对象缓存,而不是每次都读数据库。只有当数据被标示为过期时,才需要查询主事务数据库,获取数据,更新缓存。考虑到有那么多优秀开源的键—值存储系统可以作为对象缓存,所以首先强烈推荐这种方法。

除了在应用层和数据库层之间增设对象缓存之外,还可以通过复制数据库来拆分数据。大多数主要的关系数据库系统都有某种类型的复制功能。MySQL是通过主从数据库的概念来实现复制功能的。所谓主数据库就是执行写操作的主要数据库,从数据库是主数据库的只读副本。主数据库会把更新、插入、删除等操作记录在二进制的日志中。每个从数据库则是从主数据库请求二进制的日志,在自身重现这些操作。虽然这些操作是异步的,但是主数据库和从数据库中数据更新的延迟是非常小的。通常,这种实现都由几个从数据库或者只读副本构成,它们都配置在负载均衡器之后。应用向负载均衡器发起读请求,负载均衡器以循环方式或者直连方式把该请求传递给只读副本。

我们把这种类型的拆分称为X轴拆分,在图1所示的AKF扩展立方中,它被表示为“X轴—横向复制”。熟悉Web应用托管的开发者都会认同这样一个例子:在系统的Web层或应用层上,负载均衡器后的多个服务器上都运行着相同的代码。一旦负载均衡器收到请求后,它就把该请求分发到其中一个Web或应用服务器上进行处理。在应用层进行这种分发的好处是可以在负载均衡器后面放置成百上千的服务器,都运行同样的代码,处理类似的请求。

X轴原则不仅适用于数据库。Web服务器和应用服务器通常也能被轻松克隆,这样就能够把事务平均分配到多个系统上进行横向扩展。这种应用或Web服务的克隆实施起来相对比较容易,可以扩展能够处理的事务数量。遗憾的是,对于我们执行某些事务而必须操作的数据而言,该方法并不能帮助我们提高扩展性。在内存中缓存客户的专有数据或者不同功能特有的数据可能会造成扩展服务的瓶颈,很难在不影响客户响应时间的前提下扩展这些服务。要解决这种内存限制,需要利用扩展立方体的Y轴和Z轴。

原则2:拆分不同的东西(Y轴原则)

目的:有时该原则被称为通过服务或资源进行扩展,重点是扩展数据集合、事务和程序员小组。

适用情形:

  • 非常大的数据集合,数据间关系并不重要;

  • 大型的复杂系统,需要特别扩展编程资源。

应用方式:

  • 用动词拆分操作,用名词拆分资源,或者兼而有之;

  • 根据动词/名词方法的定义,拆分服务和数据。

应用理由:不仅能有效地扩展事务,还能有效地扩展与事务相关的大型数据集合。

要点:Y轴拆分,或者说面向数据/服务的拆分,能够有效地扩展事务、大型数据集合,并且有助于故障隔离。

抛开关于面向服务的架构(SOA)和面向资源的架构(ROA)这两个概念的争论,深入了解它们的基本前提就会发现,它们至少有一点是相同的,即都要求架构师和程序员考虑架构中的职责拆分。大体上就是采用动词(服务)和名词(资源)的概念来实现拆分。原则2,即扩展立方上的第二个轴,采用的就是这种方法。简而言之,原则2就是通过拆分站点中的各种功能和数据,从而实现扩展。采用原则2的简单方法就是把产品拆分为名词和动词,或者两者的组合。

首先,我们看看怎么用动词拆分站点。如果我们的站点是相对简单的电子商务站点,那么可以用动词把它拆分为注册、登录、搜索、浏览、查看、加入购物车、购买。在这些事务中,每一个事务所需要执行的数据可能都与其他事务需要的大不相同。例如,可能有人会说,注册和登录需要的数据是相同的,但其实它们都需要一些特有的数据。例如,注册可能需要检查该用户选择的ID是不是已经被别人选用了,而登录时,则无需了解其他用户的ID。注册时可能需要把大量的数据写入持久数据存储中,而登录则是一种验证用户身份的只读应用。注册可能需要用户存储许多识别个人身份的信息,包括信用卡号等,而在用户只是想建立登录连接时则无需访问这些信息。

在研究搜索和登录这两种截然不同功能时,依据动词拆分的扩展方法的不同之处以及带来的好处就更加明显了。在登录时,我们关心的通常是验证用户身份,可能会建立某些会话(这里我们采用术语会话,而不是采用状态,原因将在原则40中说明)。登录功能关心的是用户,因此需要缓存用户数据并与之进行交互操作。另一方面,搜索关心的是查找数据项,而最重要的是用户的意图(通常是用户在搜索框内输入的搜索字符串、查询或搜索项)以及我们存储在目录中的目录项。拆分这些数据集,可以使我们在系统有限的内存中缓存更多的数据,而且,由此产生的高缓存命中率也会加快事务的处理。在后端的持久性系统(如数据库)中拆分数据,就能够在这些系统中分配更多的专用内存,加速对客户(应用服务器)请求的响应。由于更好地利用了系统资源,这两个系统都会相应地更快。显然,这是扩展这些系统最容易的方法,受内存限制也更少。此外,通过采用与原则7(X轴扩展)相同的方法拆分事务,Y轴的事务扩能力也增加了。

稍等!如果我们想把用户和产品信息合并在一起,例如向客户推荐产品,又该怎么办呢?注意,这里用了新的动词——推荐。这是另一种需要拆分数据和事务的情况。我们可能会加入一种推荐服务,根据用户过去的购买行为,与具有相似购买行为的用户进行异步评估。这样可能会把数据移植到登录功能或搜索功能(当用户与系统交互时就会向他显示)。或者也可能是用户浏览器发出的一个单独的同步调用,显示在专门分配给这个推荐调用的区域。

现在可以考虑如何用名词来拆分项了。还是拿电子商务的例子来说,我们可以标识一些最终会对其进行操作的资源(而不是表示要执行的操作的动词)。我们可以认为电子商务站点是由产品目录、产品库存清单、用户账户信息、市场营销信息等构成的。采用名词拆分的方法,可以根据这些分类拆分数据,然后定义一套高级的原函数,如创建、读、更新和删除等,对这些原数据进行操作。

Y轴拆分不仅适用于扩展数据集合,还适用于扩展代码库。由于服务和资源都被拆分了,那么执行的操作和执行它们所必需的代码也会被拆分。这就意味着可以把开发复杂系统的大型编程小组拆分成各个子系统的专家组,程序员不用再担心自己必须是系统每一部分的全能专家了。当然,由于可以拆分服务,所以扩展事务也就相当容易了。

原则3:拆分相近的东西(Z轴原则)

目的:通常可以利用客户特有的属性进行拆分,如客户ID、姓名、所在地等。

适用情形:非常大的相似数据集合,如快速增长的大型客户群。

应用方式:标识你所知道的客户属性,如客户ID、姓、所在地或设备,根据这些属性拆分数据和服务。

应用理由:客户信息的增长速度超过了其他所有数据的增长,或者你需要在要扩展的某些客户群之间执行故障隔离。

要点:Z轴拆分除了有助于扩展客户群,还适用于其他不能采用Y轴拆分方法的大型数据集合。

原则3通常被称为数据分片,即把数据集合或服务分割成几片。这些数据片一般大小相同,但如果有必要的话,也有可能大小不同。这样做的原因之一就是让你推出的应用能够先只影响小部分客户,当你认为自己已经发现并解决了主要问题后,再逐渐应用于更多的客户,从而降低了风险。

通常,我们都是根据对请求者或客户的了解进行分片的。假设我们提供的是打卡和考勤管理系统,而客户是雇员数大于1000的企业级客户,我们负责对每个客户的员工进行考勤跟踪。我们可能会决定按照公司进行分片,即每个公司都有自己专用的Web页面、应用程序和数据库服务器。考虑到我们还想利用多租户架构带来的节约成本的好处,那么可以把几家小公司划分到一个数据片中。拥有许多员工的大公司可以有专用的硬件,而员工相对较少的小公司则可以共同存在于较大的数据片中。利用员工和公司之间的关系把系统划分成了可扩展的几部分,从而能够采用较少的、成本较低的硬件,实现横向扩展。

如果我们是手机广告服务提供商,就必须了解终端用户所用设备及其运营商,两者都很引人注目,都可用于划分数据。如果我们是电子商务运营商,那么可以根据用户的所在地划分用户群,这样能有效地利用配送中心的可用库存。或者也可以根据客户的新老程度、购买次数和购买金额划分客户。如果这些方法都失败了,那么可以利用在用户注册时分配给他的用户ID的模数或散列表进行划分。

为什么要拆分相近的东西呢?对于高速增长的公司来说,答案显而易见。响应请求的速度部分是由远近位置不同的缓存的缓存命中率决定的。该速度决定了一个系统能够处理多少个事务,从而决定了处理一定数量的请求,需要多少个系统。一种极端情况,是对数据不做划分,那么当我们要响应一个用户的请求,从而需要遍历一块巨大的数据时,事务处理的速度会慢得令人难以忍受。当响应请求的速度至关重要,而响应请求要查询的数据巨大时,拆分不同的东西(原则2)或者拆分相近的东西(原则3)就势在必行了。

拆分相近的东西显然并不局限于拆分客户,但是根据我们的咨询经验,拆分客户是实施原则3最常见也最简单的方法。有时,我们也推荐拆分产品目录。不过对于要把各种各样的产品目录拆分成草坪躺椅和尿布这样的数据项的情况,我们会把它归为拆分不同的东西。我们也曾经帮助客户利用事务ID的模数或散列表进行划分。在这种情况下,我们对请求者一无所知,但我们却有一个能够利用的单一增长的数值。对于要保存事务日志以便将来能够研究其中错误的系统,可以采用这种类型的 划分。

小结

本文提出的三个简单原则几乎可以帮你扩展任何东西。虽然扩展系统和平台的方法有很多种,但有了这三个原则,那么在你前进的道路上,就没有什么与扩展相关的障碍了。

通过克隆进行扩展——通过克隆或复制数据和服务,可以轻松地扩展事务。

通过拆分不同的东西进行扩展——用名词和动词标识数据和服务,从而进行划分。如果拆分正确,那么事务和数据集都能得到有效扩展。

通过拆分相近的东西进行扩展——通常拆分的是数据集。把客户划分到专用的独立的数据片或泳道,可以对事务和数据进行扩展。


本文节选自《高扩展性网站的50条原则》

转载声明:本文转载自异步社区

原文链接:https://www.epubit.com/articleDetails?id=NC7E3EF9387100001BA721A90179310DF

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。