扩展性意味着,系统有可伸缩性/弹性,度量增加
系统处理能力的指标。
扩展性并不单单是系统能力本身,而是伸缩性&性能&成本等的综合考量的结果。
系统处理能力(系统性能):
延迟 - 系统处理单词请求所需时间;
吞吐量 - 单位时间内系统处理次数。
吞吐量越高越好,延迟越低越好
。
高伸缩性:
添加资源就可以应对处理能力需求增长;
意味着用户/流量/数据增长,性能指标不下降
。
伸缩性差:
单用户性能正常;用户增长后性能指标下降;性能提升的成本高。
Scale out,增加更多机器,提高系统整体性能和处理能力。
更符合分布式系统架构设计。
横向扩展,理论上可以无限扩展。但是受到经济成本、法律法规、物理限制(机房大小)的限制。
Scale up,通过增加/强单机资源,提升单机性能,从而提高系统整体性能。(提升CPU/内存/存储设备等)
纵向扩展,一般是单体系统(集中式架构)。特点是上限低成本高。
X轴:
只要系统可以复制,就可以使用X轴扩展。
原点就是起点,系统最初都是单机版。将原始系统改造为share nothing(无状态),复制并load balance(负载均衡)
,状态都存在于数据库中。
优点:简单易行。
缺点:数据库为单点/无法应对高复杂度系统。
Y轴:
把系统根据功能/组件等进行拆分,并以此各自扩展。
要找到Bounded context(业务边界)。比如说微服务改造,代码拆分、数据拆分。
优点:当X轴扩展无法满足需求时,使用Y轴扩展。微服务化后,系统更内聚。Fault isolation(故障隔离 )。
缺点:改造难度高,成本高。
Z轴:
按照用户属性来切分系统,以实现各自扩展。
业务切分,如地理位置、用户类别等(CDN分地域)。数学切分,如用户ID、IP等(ID取模)。
优点:可以根据不同的用户提供不同的服务,更进一步的fault isolation(故障隔离),可与Y轴扩展联合使用。
缺点:不能降低系统复杂度,反而增加运维复杂度。
原子性(Atomicity):原子性是指事务是一个不可分割的工作单位,事务中的操作要么全部成功,要么全部失败。比如在同一个事务中的SQL语句,要么全部执行成功,要么全部执行失败。
一致性(Consistency):官网上事务一致性的概念是:事务必须使数据库从一个一致性状态变换到另外一个一致性状态。
换一种方式理解就是:事务按照预期生效,数据的状态是预期的状态。
隔离性(Isolation):事务的隔离性是多个用户并发访问数据库时,数据库为每一个用户开启的事务,不能被其他事务的操作数据所干扰,多个并发事务之间要相互隔离。
持久性(Durability):持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来即使数据库发生故障也不应该对其有任何影响。
分布式系统为达成高可用,高扩展
,需要:
数据分主/从(或源/副本);
写入主库成功,用户读取从库,数据不是最新的(数据不一致);
从库同步成功,读取主从库都是最新数据(数据最终一致);
保证整体系统最终都是最新数据从而实现一致性,即最终一致性。
从不一致到一致的时间无论长短,都是最终一致性。
相对于数据库ACID的一致性,最终一致性是弱一致性。
高可用意味着,系统不能提供服务的时间趋近于零。
度量高可用的指标:
MTBF(Mean Time Between Failure) - 平均无故障工作时间。
MTTR(Mean Time To Repair) - 平均修复时间。
A = MTBF/(MTBF+MTTR) - 可用度计算方式。(意味着尽量增加机器工作时间,减少机器宕机时间,减少修复时间)
高可用解决方案:冗余。
在给定的时间间隔和给定条件下,系统可以无故障持续运行的概率。
影响可靠性的因素:
所有能引起故障的因素;
故障率越高可靠性越低;
重点在故障次数。
高可靠跟高可用不是一个概念,可用性是计算停机时间,可靠性是计算故障次数。
比如说,一个系统一月内有一次宕机了6小时,它不一定是一个高可用系统,但是可靠性还是能保证的,因为只宕机了一次。
在比如说,一个系统一天内不停的重启,每次重启1秒钟,一天重启十几次。它可能是个高可用系统,但是不是高可靠系统。
可靠性(Reliability)是可用性的一个子集。也就意味着,达到高可用系统要比高可靠系统难。
稳定性:在一个运行周期内、一定条件下,在持续操作时间内出错的概率,性能劣化趋势
。
所以,稳定性有两个维度,就是出没出错,还有就是系统有没有越来越慢。
系统响应一致,行为是否稳定
,也是稳定性判定的一个维度。
Availability(可用性)是Stability(稳定性)的子集。
高可靠性最容易达到,高稳定性最难达到。
在错误发生时,系统依然能够提供正确的功能的能力。
容错能力正是为了提高系统的可靠性、稳定性、可用性。
解决方案:
空间冗余(Space redundancy) - 提供额外的组件、功能或数据(硬件/软件/信息)。
时间冗余(Time redundancy) - 重试(重新计算/重新传输)。
分布式系统:一组独立的计算单元通过网络连接并透明地对外提供统一服务。
特点:
组件可以支持各自独立开发。
对用户透明,用户感知是一个系统而非多个组件。
组件间通过消息进行通信。
易于扩展。
应对增长:传统架构无法处理日益增长的互联网用户需求。
扩容:需要新架构更进一步提升系统的扩展能力。
系统稳定性:新架构需要高可用、相对独立和故障隔离使整体系统更稳定。
灰度发布:系统和组件都纳入版本管理,按需部署并进行灰度发布。
(1)核心问题:应用扩展性。
应用由单体进化到分布式。
应用扩展达到上限。
数据库无法支撑应用层的扩展。
(2)核心问题:数据中心扩展性。
单数据中心上限受物理因素限制。
系统需求超过单数据中心如何扩展。
应用如何在多数据中心灵活切换。
(3)核心问题:容错性。
应用:组件冗余、数据冗余、时间冗余(重试)。
数据中心:系统冗余(多地部署),数据冗余(数据复制)。
分布式方式部署,总会有一些局限性,所以单元化架构应运而生。
系统以服务的形式满足内外的业务需求。
以SOA或微服务架构为技术指导原则来构建各种服务。
按照特定规则将不同服务分配到不同层,并定义层与层之间的依赖(接口)
层自主权高、逻辑清晰、单向依赖,满足大部分系统需求。
传统的分层架构,架构层的扩展上限受诸多因素制约。
分层架构虽然是一个非常经典的架构风格,但是有诸多限制,比如说微服务不能无限拆分,层也不能无限扩展。
所以分段架构出现了。
Agile开发方式(敏捷开发)、虚拟化和容器化可以在同一架构层中通过隔离和自主权划分为更细粒度的逻辑段(Segment)。
根据业务能力,在每一层中将不同组件划分到不同段中。
实现手段:Runtime partitioning(按照功能划分)、Multi-tenancy(多租户)、Platform as a service(平台即服务)。
分段架构即使是比分层架构进步了,但是仍然无法达到一个自由扩展的、自包含的去中心化系统。
单元化架构:将系统按数据特征和功能垂直地划分为一个个单元。
一个单元就是一个能完成所有业务操作的自包含集合(包含业务所需的所有服务,数据及资源)。
一个单元可以被独立地部署、管理及监控(部署在一个或多个数据中心,提升整体系统的扩展上限)。
单元化架构下,系统仍然分层(层的任意组件都属于且仅属于同一单元,上层调用下层时仅依赖于本单元内的层)。
单元化架构可以理解为集装箱性质的,里面有很多组件,这些组件都生存在同一个容器中(单元中),这些单元是可以独立部署的。
用户就是与系统交互的人。
将系统拆解单元化时,用户是很重要的参考因素。按照用户的属性、地域
等特征将系统单元化。(也就是说,按照用户进行单元的划分)
组件是单元化架构里最小原子单位。
一个单元就是由若干个组件构成的。
一个组件就是代表一组业务功能或服务。
组件自定义范围和形态,在其定义域内独立运行。
组件依赖于其它组件,也可以被其他组件依赖。
确保百分之90以上的功能都是在本单元完成的,如果有跨单元的功能一定要谨慎。
一个单元由一个或多个组件组成。同时,一个单元本身也可以看成一个组件,而且是可以集中部署不可分割的组件。
一个单元就是一组从设计到实现被分配到同一个部署单位的组件。
一个单元就是一个可以被构建、部署和管理的不可分割的完备组件。
所有单元都必须被命名,且全局唯一。
命名的主要作用是为后续发布和管理。
单元也会迭代和进化,必须以版本管理的形式来控制。
单元内的组件也必须被命名和版本化管理。
所有单元必须是独立的,才能达成容错/容灾的目的。
所有单元是可以被独立部署。
设计单元时,各单元功能是独立的。(逻辑无重叠,避免不必要的依赖)
一个业务功能在一个单元内完成,避免跨单元功能。
按逻辑划分单元,相关业务场景应置于同一单元,达成逻辑完备。
一个单元能提供的功能,其所需服务和数据都要划分到同一单元内。
跨单元访问要谨慎考虑,尽量不要跨单元访问。
一个单元就是一个可以被独立部署的组件。
设计单元的最终目的是该单元能够独立部署。
三级迭代:单元内各组件可以独立迭代;各个单元可以独立迭代;系统作为多个单元的集合可以整体迭代。
一个单元由一组或多组组件构成,并对外申明其功能。
单元的功能必须提供某种网络访问接口,内部的组件不能直接暴露,只能通过统一的ingress(入口)对外提供服务。
如果单元对外界有依赖,必须通过egress(出口)进行访问。
单元内的组件可以通过允许的通信协议互相访问。
异地多活:
系统功能和用户所在地域有强关联性。(外卖、物流、所有O2O等)
高可用要求最高,不能大规模长时间宕机的系统。(如银行、电信、证券等系统)
互联网用户天然分布式、多地数据中心以提高访问速度。(视频流图片等CDN技术)
预防极端气候现象。(地震、飓风、水灾引起的数据中心整体不可用)
突破扩展上限:
系统能力或用户数发展至单机房/单数据中心成为瓶颈。
多机房部署而不改造架构,引起的跨机房调用性能降低。
数据库主库单点,连接数有限,不能支持应用的持续发展。(超十亿用户,微信、支付宝、Google、Facebook、Whatsapp等)
对开发者透明:在做实现时,不依赖于单元划分和部署。
对组件透明:在组件运行时,不感知其承载单元。
对数据透明:数据库并不知道为哪个单元提供服务。
系统业务复杂度足够高。
系统可以按照某一维度进行切分。
系统数据必须可以分区。
同一业务功能必须在单元内完成。
同一业务操作所需数据也在该功能单元内。
尽量避免跨单元的依赖。
系统切分:把原有系统按照单元化架构的要求(功能、用户、数据)进行切分,把整个系统的功能拆散到不同的单元中。
流量路由:当我们把系统拆分成若干个小的单元以后,用户访问我们系统时,必须找到要访问的单元,这个过程叫路由。(通过公有云、中间件等)
数据复制:考虑数据一致性要求、数据的延迟性要求。
DDD切分领域服务、数据和业务流程:
切分出的服务和数据属于同一单元。
出现交集(重叠)时,以单元间访问量做决策指标。
业务流程所需组件和数据划分在同一单元:
一个业务流程在同单元执行,保证性能和稳定性。
尽量避免跨单元的服务访问和数据库读写。
同单元高内聚,单元间低耦合。
与组织架构和核心业务流程强相关:
核心业务保证高可用,高增长必须单元化部署。
边缘业务随核心业务数据分配单元。
功能粒度要适当:
粗粒度单元,轻易超过单元资源承载上限,需要二次拆分。
细粒度单元,单元内资源浪费,性价比低。
开发团队随单元化架构调整,整个过程要持续迭代。
用户属性对系统核心功能有决定性作用:
外卖、叫车、共享等业务严重依赖于用户的地域属性。
VIP用户、大买家等不同类型的用户。
新建用户按规则自动分配单元,存量数据按规则做数据迁移:
相同属性的数据聚集在同一单元。
一个单元内完成系统所有核心业务,单元内不再按功能拆分。
数据ID的规则:
不同单元同时产生数据,且数据有同步需求,ID必须保证跨单元不重复。
由ID可以按规则推算所属单元。
用户属性划分导致单元规模层次不齐:
过于庞大的单元,二次拆分。
合并多个小单元为一个常规单元。
当用户属性改变时,系统自动同步数据:
该类属性一定要是低频变化。
系统同步速度一定高于属性变化速度。
否则,数据拆分一定是有问题的。
按照数据某种属性进行单元划分:
无业务含义,不可更改,不重复,一般选ID或者时间戳(取模)。
该切分方式水平扩展上限高。
账户、订单、流水、商户、商品等比较易于切分,可以根据属性进行切分。
数据可以通过规则推算所属单元。
设计规则使数据均分:
单元分配的数据比例等于其整体流量比例。
这种按照数学计算的方式,比按照业务切分的方式,数据更平均。
无业务依赖,跨单元访问不可控,解决方案:
引入中间件实现开发者透明。
无法切分的数据,高频访问,创建特殊附属单元:
该单元必须与切分单元在同一数据中心/机房,以降低访问延时。
任意切分单元对附属单元的写入都会同步到其他附属单元。
大部分系统都是读多写少,写复制的成本远低于高频读操作。
数据实时性要求高的系统可能会失败。
无法切分的数据,低频访问,系统共享同一单元:
低频读写,时延不影响主业务流程,代价可接受。
根据数据中心距离,选择与所有数据中心都相对最近的数据中心作为共享单元的部署地。
单元内部垂直切分:
单元如果有瓶颈,优先考虑内部垂直切分。
综合库:
创建单独的综合库,同步汇总所有单元的数据,供非实时业务使用。
组件:
就是服务,业务逻辑。
不直接被单元外访问。
独立构建、发布。
数据:
仅限该单元数据。
中间件:
内部通信。
与单元路由有关的中间件。
反向代理:
单元内服务的反向代理服务器,供外部访问。
单元选择功能的路由器。
安全和控制策略的中心。
网关:
单元内访问外部服务的代理服务器。
单元选择功能的代理服务器。
为不同数据中心的不同单元分配不同域名:
建立全局路由规则服务。
或客户端登陆后,本地计算所属单元(或数据中心)。
客户端直接访问所属单元。
针对未改造客户端、匿名用户等:
利用公有云就近提供服务。
由业务服务设置Cookie标志指定所属单元。
tag、Cookie或URL参数指定要访问单元:
公有云或CDN提供此服务。
单元网关也是该单元的反向代理服务器。
单元网关根据规则计算请求所属单元:
属于当前单元,直接路由请求到正确的组件(服务)。
属于其他单元,转发到对应的正确单元并上报路由错误信息。
无法判定,根据路由规则发送给组件,由业务逻辑进一步分辨。
tag、Cookie或URL参数指定要访问单元。
统一封装的开发框架处理请求,在拦截器或者过滤器层对开发者透明:
根据请求计算所属单元,属于本单元则继续处理。
不属于本单元则转发。
无法判定就交予业务逻辑层或数据库访问层来决定。
业务层:
普通对单元无感知的业务,按原有逻辑直接处理。
依赖于单元信息的业务,可以自行计算或从容器注入单元信息。
应用层操作Cookie设置单元信息,方便后续单元网关路由。
远程调用框架要透明化支持自动单元路由:
RPC。
HttpClient。
根据请求计算出正确的目标单元并自动路由。
数据访问层:
最后防线。
通过数据访问的驱动程序或框架改造,使其对开发者透明。
如果有误,将错误的访问路由到正确的数据库(或表)。
单元化的数据为本单元组件服务:
依赖决定是否需要复制数据。
单元间相互复制。
通过数据中台复制。(数据总线)
注意复制的数据量要少,数据的id不能重复。
根据业务场景选择:
强一致/最终一致。
最大数据延迟。
读写失败对业务的影响。
关系型数据库复制:MySQL、Oracle。
NoSQL数据库复制:MongoDB、Redis。
消息中间件复制:Kafka,RabbitMQ,RocketMQ。