一文读懂分布式系统
什么是分布式系统?
**分布式系统(Distributed system)是由一组计算机程序,通过网络相互连接传递消息与通讯并协调它们的行为而形成的系统**。
分布式系统的出现是为了用廉价的、普通的机器完成单个计算机无法完成的计算、存储任务。其目的是利用更多的机器,处理更多的数据,同时能消除系统的瓶颈或中心故障点。
为什么需要分布式系统?
我们先从系统演进开始讲起,分布式系统出现主要解决集中式系统无法解决的问题,接下来我们从一个开发角度阐述分布式出现的原因。
最早,没有分布式系统之前所有系统都运行在单机上,单机处理用户的请求以及用户数据的存储。
单机系统
先从大家最熟悉的web后台应用讲起,以前我们的系统访问量小,业务也不复杂,一台服务器一个应用就可以处理所有的业务请求量。后台随着业务发展访问量增加,单台机器无法承载大的并发量。
垂直扩展
为提高系统处理能力,我们首先想到的扩展方式就是升级系统配置,8 核 cpu 升级为 32 核,64 核,内存 64G 升级为 128G,256G,带宽上万兆,十万兆,这就叫做垂直扩展
。
如果我们的服务器可以无限添加配置,那么一切性能问题都不是问题
但这样的扩展终将无法持续下去,原因如下:
- 单机系统的处理能力最终会达到瓶颈
- 单机升级的边际成本将越来越大
水平扩展
当垂直扩展
到达技术瓶颈或投入产出比超过预期,我们可以考虑通过增加服务器数量来提高并发能力,这种方式就是水平扩展
在水平扩展
中,我们增加了服务器数量,但是如何让这些服务器像一个整体一样对外提供稳定有效的服务才是关键。既然已经有了多台服务器,我们就要考虑如何将系统部署到到不同的节点上去。通常我们将我们的SpringBoot 项目部署到多台服务器上,前面加个 nginx 就可以了
系统拆分
无论是垂直扩展
还是水平扩展
,系统依然存在如下问题:
- 所有服务依然在共享一个数据库
- 服务的调用需要依赖一些第三方中间件。比如上图nginx
垂直拆分
系统的垂直拆分
,就是将相同的系统部署多套,所有的节点并没有任何不同,角色和功能都一样,它们各自分担一部分功能请求,这样整个系统的处理能力的上升了。
从处理 web 请求上来看,垂直拆分
的每个节点都处理一个完整的请求,每个节点都承担一部分请求量。
从数据存储的角度看,每个数据节点都存储相同的业务数据,每个节点存储一部分数据。
水平拆分
系统的水平拆分
,就是将系统按不同模块或角色拆分,不同的模块处理不同的事情。
从 web 请求上来看,需要多个相互依赖的系统配合完成一个请求,每个节点处理的需求不一致。
从数据存储角度上来看,每个数据节点都存储着各自业务模块相关的数据,它们的数据都不一样。
分布式定义
上面垂直拆分
之后各个节点组成的就是一个集群
,而水平拆分
各个节点就是分布式
,这就是集群
和分布式
的区别。
集群
除了上面提到的可以提高并发处理能力外,还可以保证系统的高可用,当一部分节点失效后,整个系统依旧可以提供完整的服务。分布式
也一样,除了提高并发能力,解耦系统,使系统边界更清晰,系统功能更内聚也是其一大好处,所以在实际的系统中我们往往这两种方式同时都在使用,而且我们常常提及的分布式系统
其实是包含着集群
的概念在里面的。
分布式系统有什么特点
首先,按照业务的架构层次栈,我自底向上按照资源、通信、数据与计算的维度,梳理出了 4 个技术层次:
- 分布式资源池化
- 分布式通信
- 分布式数据存储与管理
- 分布式计算
这样的划分符合业务架构设计的一般规律,即在一定资源上,进行一定通信,通过一定计算,完成一定数据的加工和处理,从而对外提供特定的服务。
基于横向技术在分布式环境下,无论是资源、通信、数据还是计算,都需要去解决协同、调度、追踪高可用,还有部署的问题。所以衍生出相应问题需要去解决:
-
分布式协同
-
分布式调度
-
分布式追踪与高可用
-
分布式部署
分布式系统技术
前面讲过分布式系统一些特点,比如:分布式协同、分布式调度。那么分布式系统如何进行协同和调度。这里,我拿Google GFS对分布式系统一些特点做一些具像化解释。
上图是大数据运用分布式需要采用技术。首先,我们先对这些名词做一些解释。
数据复制
复制主要指通过互联网网络在多台机器上保存相同的数据副本,通过数据复制方案,人们通常系统达到以下目的:
- 使数据在地理位置上更接近用户,从而降低访问延迟
- 当部分组件出现故障,系统依然可以继续工作,从而提高可用性
- 扩展至多台机器以同时提供数据访问服务,从而提高吞吐量
数据分区
复制主要解决了可用性,提升了系统吞吐量。然而,面对一些海量数据集系统面临非常高的查询压力,复制技术解决不了。所以,我们还需要将数据拆分成为分区(也称分片)
,采用数据分区主要目的是提高系统可扩展性
通常分区主要与复制结合使用,既每个分区在多个节点上都有副本。
2阶段提交(2PC)
二阶段提交协议(Two-phase Commit,即 2PC)是常用的分布式事务解决方案,即将事务的提交过程分为两个阶段来进行处理。
两个阶段分别为:
- 准备阶段
- 提交阶段
参与的角色:
- 事务协调者(事务管理器):事务的发起者
- 事务参与者(资源管理器):事务的执行者
准备阶段(投票阶段)
这是两阶段的第一段,这一阶段只是准备阶段,由事务的协调者发起询问参与者是否可以提交事务,但是这一阶段并未提交事务,流程图如下图:
- 协调者向所有参与者发送事务内容,询问是否可以提交事务,并等待答复
- 各参与者执行事务操作,将 undo 和 redo 信息记入事务日志中(但不提交事务)
- 如参与者执行成功,给协调者反馈同意,否则反馈中止
提交阶段
这一段阶段属于2PC的第二阶段(提交 执行阶段),协调者发起正式提交事务的请求,当所有参与者都回复同意时,则意味着完成事务,流程图如下:
- 协调者节点向所有参与者节点发出正式提交(
commit
)的请求。 - 参与者节点正式完成操作,并释放在整个事务期间内占用的资源。
- 参与者节点向协调者节点发送ack完成消息。
- 协调者节点收到所有参与者节点反馈的ack完成消息后,完成事务。
但是如果任意一个参与者节点在第一阶段返回的消息为终止,或者协调者节点在第一阶段的询问超时之前无法获取所有参与者节点的响应消息时,那么这个事务将会被回滚,回滚的流程图如下:
- 协调者节点向所有参与者节点发出回滚操作(
rollback
)的请求。 - 参与者节点利用阶段1写入的undo信息执行回滚,并释放在整个事务期间内占用的资源。
- 参与者节点向协调者节点发送ack回滚完成消息。
- 协调者节点受到所有参与者节点反馈的ack回滚完成消息后,取消事务。
不管最后结果如何,第二阶段都会结束当前事务。
CAP理论
CAP 理论对分布式系统的特性做了高度抽象,形成了三个指标:
- 一致性(Consistency)
- 可用性(Availability)
- 分区容错性(Partition Tolerance)
一致性
一致性说的是客户端的每次读操作,不管访问哪个节点,要么读到的都是同一份最新写入的数据,要么读取失败。
你可以把一致性看作是分布式系统,对访问自己的客户端的一种承诺:不管你访问哪个节点,要么我给你返回的都是绝对一致的最新写入的数据,要么你读取失败。你可以看到,一致性强调的是数据正确。
为了帮你理解一致性这个指标,我给你举一个具体的例子。比如,2 个节点的 KV 存储,原始的 KV 记录为“X = 1”。
紧接着,客户端向节点 1 发送写请求“SET X = 2”。
如果节点 1 收到写请求后,只将节点 1 的 X 值更新为 2,然后返回成功给客户端。
那么,此时如果客户端访问节点 2 执行读操作,就无法读到最新写入的 X 值,这就不满足一致性了。
如果节点 1 收到写请求后,通过节点间的通讯,同时将节点 1 和节点 2 的 X 值都更新为 2,然后返回成功给客户端。
那么在完成写请求后,不管客户端访问哪个节点,读取到的都是同一份最新写入的数据,这就叫一致性。
一致性这个指标,描述的是分布式系统非常重要的一个特性,强调的是数据正确。也就是说,对客户端而言,每次读都能读取到最新写入的数据。
不过集群毕竟不是单机,当发生分区故障的时候,有时不能仅仅因为节点间出现了通讯问题,无法响应最新写入的数据,之后在客户端查询数据时,就一直返回给客户端出错信息。这句话怎么理解呢?我来举个例子
业务集群中的一些关键系统,比如名字路由系统(基于 Raft 算法的强一致性系统),如果仅仅因为发生了分区故障,无法响应最新数据(比如不满足“大多数”,没有了领导者),为了不破坏一致性,那么客户端查询相关路由信息时,系统就一直返回给客户端出错信息,此时相关的业务都将因为获取不到指定路由信息而不可用、瘫痪,这可以说是灾难性的故障了。
可用性
这个时候,我们就需要牺牲数据正确,每个节点使用本地数据来响应客户端请求,来保证服务可用,这就是我要说的另外一个指标,可用性。
可用性说的是任何来自客户端的请求,不管访问哪个非故障节点,都能得到响应数据,但不保证是同一份最新数据。你也可以把可用性看作是分布式系统对访问本系统的客户端的另外一种承诺:我尽力给你返回数据,不会不响应你,但是我不保证每个节点给你的数据都是最新的。这个指标强调的是服务可用,但不保证数据正确。
我还是用一个例子,帮助你理解一下。比如,用户可以选择向节点 1 或节点 2 发起读操作,如果不管节点间的数据是否一致,只要节点服务器收到请求,就响应 X 的值,那么,2 个节点的服务是满足可用性的。
分区容错性
最后的分区容错性说的是,当节点间出现任意数量的消息丢失或高延迟的时候,系统仍然在继续工作。也就是说,分布式系统在告诉访问本系统的客户端:不管我的内部出现什么样的数据同步问题,我会一直运行。这个指标,强调的是集群对分区故障的容错能力。
来看下面的图,当节点 1 和节点 2 通信出问题的时候,如果系统仍能继续工作,那么,2 个节点是满足分区容错性的。
因为分布式系统与单机系统不同,它涉及到多节点间的通讯和交互,节点间的分区故障是必然发生的,所以我要提醒你的是,在分布式系统中分区容错性是必须要考虑的。
现在你了解了一致性、可用性和分区容错性,那么你在设计分布式系统时,是选择一致性?还是可用性?还是分区容错性?还是都可以选择呢?这三个特性有什么冲突么?这些问题就与我接下来要讲的“CAP 不可能三角”有关了。
CAP 不可能三角
CAP 不可能三角说的是对于一个分布式系统而言,一致性(Consistency)、可用性(Availability)、分区容错性(Partition Tolerance)3 个指标不可兼得,只能在 3 个指标中选择 2 个。