前言
文章主要参考的是分布式系统 - 理论基础,理论及一致性算法,之所以二次编辑,是为了增加部分自己理解的内容。
一个分布式系统是一些独立的计算机集合,但是对这个系统的用户来说,系统就像一台计算机一样。
什么是分布式系统?
一个分布式系统是一些独立的计算机集合,但是对这个系统的用户来说,系统就像一台计算机一样。
分布式系统是一个硬件或软件组件分布在不同的网络计算机上,彼此之间仅仅通过消息传递进行通信和协调的系统。简单来说就是一群独立计算机集合共同对外提供服务,但是对于系统的用户来说,就像是一台计算机在提供服务一样。分布式意味着可以采用更多的普通计算机(相对于昂贵的大型机)组成分布式集群对外提供服务。计算机越多,CPU、内存、存储资源等也就越多,能够处理的并发访问量也就越大。
从分布式系统的概念中我们知道,各个主机之间通信和协调主要通过网络进行,所以分布式系统中的计算机在空间上几乎没有任何限制,这些计算机可能被放在不同的机柜上,也可能被部署在不同的机房中,还可能在不同的城市中,对于大型的网站甚至可能分布在不同的国家和地区。
分布式系统的主要特征
无论空间上如何分布,一个标准的分布式系统应该具有以下几个主要特征
- 分布性
分布式系统中的多台计算机之间在空间位置上可以随意分布,同时,机器的分布情况也会随时变动。
- 对等性
分布式系统中的计算机没有主/从之分,即没有控制整个系统的主机,也没有被控制的从机,组成分布式系统的所有计算机节点都是对等的。副本(Replica)是分布式系统最常见的概念之一,指的是分布式系统对数据和服务提供的一种冗余方式。在常见的分布式系统中,为了对外提供高可用的服务,我们往往会对数据和服务进行副本处理。数据副本是指在不同节点上持久化同一份数据,当某一个节点上存储的数据丢失时,可以从副本上读取该数据,这是解决分布式系统数据丢失问题最为有效的手段。另一类副本是服务副本,指多个节点提供同样的服务,每个节点都有能力接收来自外部的请求并进行相应的处理。
- 自治性
分布式系统中的各个节点都包含自己的处理机和内存,各自具有独立的处理数据的功能。通常,彼此在地位上是平等的,无主次之分,既能自治地进行工作,又能利用共享的通信线路来传送信息,协调任务处理。
- 并发性
在一个计算机网络中,程序运行过程的并发性操作是非常常见的行为。例如同一个分布式系统中的多个节点,可能会并发地操作一些共享的资源,如何准确并高效地协调分布式并发操作也成为了分布式系统架构与设计中最大的挑战之一。
分布式系统面临的问题
- 缺乏全局时钟
在分布式系统中,很难定义两个事件究竟谁先谁后,原因就是因为分布式系统缺乏一个全局的时钟序列控制。
- 机器宕机
机器宕机是最常见的异常之一。
- 网络异常
消息丢失:两片节点之间彼此完全无法通信,即出现了“网络分化”; 消息乱序:有一定的概率不是按照发送时的顺序依次到达目的节点,考虑使用序列号等机制处理网络消息的乱序问题,使得无效的、过期的网络消息不影响系统的正确性; 数据错误;不可靠的TCP,TCP 协议为应用层提供了可靠的、面向连接的传输服务,但在分布式系统的协议设计中不能认为所有网络通信都基于TCP 协议则通信就是可靠的。TCP协议只能保证同一个TCP 链接内的网络消息不乱序,TCP 链接之间的网络消息顺序则无法保证。
- 分布式三态
如果某个节点向另一个节点发起RPC(Remote procedure call)调用,即某个节点A 向另一个节点B 发送一个消息,节点B 根据收到的消息内容完成某些操作,并将操作的结果通过另一个消息返回给节点A,那么这个RPC 执行的结果有三种状态:“成功”、“失败”、“超时(未知)”,称之为分布式系统的三态。
- 存储数据丢失
对于有状态节点来说,数据丢失意味着状态丢失,通常只能从其他节点读取、恢复存储的状态。 异常处理原则:被大量工程实践所检验过的异常处理黄金原则是:任何在设计阶段考虑到的异常情况一定会在系统实际运行中发生,但在系统实际运行遇到的异常却很有可能在设计时未能考虑,所以,除非需求指标允许,在系统设计时不能放过任何异常情况。
衡量分布式系统的指标
- 性能
系统的吞吐能力,指系统在某一时间可以处理的数据总量,通常可以用系统每秒处理的总的数据量来衡量;系统的响应延迟,指系统完成某一功能需要使用的时间;系统的并发能力,指系统可以同时完成某一功能的能力,通常也用QPS(query per second)来衡量。上述三个性能指标往往会相互制约,追求高吞吐的系统,往往很难做到低延迟;系统平均响应时间较长时,也很难提高QPS。
- 可用性
系统的可用性(availability)指系统在面对各种异常时可以正确提供服务的能力。系统的可用性可以用系统停服务的时间与正常服务的时间的比例来衡量,也可以用某功能的失败次数与成功次数的比例来衡量。可用性是分布式的重要指标,衡量了系统的鲁棒性,是系统容错能力的体现。
- 可扩展性
系统的可扩展性(scalability)指分布式系统通过扩展集群机器规模提高系统性能(吞吐、延迟、并发)、存储容量、计算能力的特性。好的分布式系统总在追求“线性扩展性”,也就是使得系统的某一指标可以随着集群中的机器数量线性增长。
- 一致性
分布式系统为了提高可用性,总是不可避免的使用副本的机制,从而引发副本一致性的问题。越是强的一致的性模型,对于用户使用来说使用起来越简单。
分布式理论基础
CAP理论
CAP理论是分布式系统、特别是分布式存储领域中被讨论的最多的理论。其中C代表一致性 (Consistency),A代表可用性 (Availability),P代表分区容错性 (Partition tolerance)。CAP理论告诉我们C、A、P三者不能同时满足,最多只能满足其中两个。
C - Consistency (一致性)
在分布式系统中的所有数据备份,在同一时刻是否同样的值。
通俗理解:强一致性。比如你往节点 A 写入一条数据,哪怕毫秒级之后去读节点 B,也必须能读到刚才写入的这条数据。如果节点 B 还没同步好,它必须阻塞等待,或者报错,不能返回旧数据。
A - Availability (可用性)
请求能够及时处理,不会一直等待,即使出现节点失效。
通俗理解:系统必须一直“在线”并能正常回复。不管内部数据是不是最新的,只要服务没挂,就得给我返回结果(哪怕是旧数据),不能报错说“服务不可用”或一直卡死。
P - Partition Tolerance (分区容错性)
系统在遇到任何网络分区故障(即节点之间无法通信)时,仍然需要能继续运行。
通俗理解: 只要网络断了(光纤挖断、交换机故障),把系统分成了两个互不相通的孤岛,系统还得能干活,不能整个瘫痪。
BASE理论
BASE是“Basically Available, Soft state, Eventually consistent(基本可用、软状态、最终一致性)”的首字母缩写。其中的软状态和最终一致性这两种技巧擅于对付存在分区的场合,并因此提高了可用性。它是为了解决互联网系统在海量数据和高并发场景下,必须牺牲部分强一致性来换取高可用性(Availability)和高性能而提出的。
BASE 基本上等于 CAP 中的 AP + 最终一致性
- CAP 说: 选了 AP,数据可以不一致。
- BASE 说: 没错,数据现在的确不一致,但我承诺过一会儿(Eventual)它会变一致。
So
BA - Basically Available (基本可用)
这是 BASE 理论中最实用的一点。它承认系统在出现不可预知的故障(网络波动、流量洪峰、机房断电)时,允许损失部分可用性,但系统不能完全挂掉。
这种“损失”通常体现在两个方面:
-
响应时间上的损失(延时):
- 正常情况下,搜索一个商品只需要 0.5 秒。
- 在“基本可用”状态下(比如双11高峰),系统压力大,允许响应时间延长到 3 秒。用户虽然觉得卡,但还能用。
-
功能上的损失(降级):
- 正常情况下,你打开电商首页,能看到精美的“猜你喜欢”推荐列表。
- 在“基本可用”状态下,为了保护核心下单服务,系统把计算量大的“推荐服务”暂时关掉,给你展示一个静态的默认列表。
- 核心逻辑: 丢卒保车。只要核心功能(下单、支付)能用,边缘功能(评论、推荐)可以暂时不可用。
S - Soft state (软状态)
它与“硬状态”(Hard State,即 ACID 中的强一致性)相对。
硬状态: 数据必须原子性地从 0 变到 1。在变化过程中,外部看不见中间状态。
- 软状态: 允许系统存在中间状态。
- 承认不同节点之间的数据同步存在滞后(Lag) 。
- 允许用户在这一小段时间内,看到“正在处理中”或者“旧数据”。
- 例子: 银行转账时,钱从 A 扣了,还没到 B 账上。此时系统的状态可能显示为“转账处理中”。这个状态本身就是“软状态”,它不会永久存在,最终会变成“成功”或“失败”。
E - Eventual consistency (最终一致性)
这是 BASE 理论的最终目标。
定义: 系统不保证在写入发生的那个瞬间,所有节点都能读到最新数据。但在经过一段有限的时间(Time Window)后,如果没有新的更新,所有副本的数据最终会达到一致。
关键点: 这个“时间窗口”多长?取决于网络延迟、系统负载和数据复制方案。可能是几毫秒,也可能是几分钟。
例子:
- 在 12306 买了一张票,显示“购票成功,正在出票”。
- 此时去数据库查,可能库存还没完全扣减同步(软状态)。
- 但过了几秒钟,收到短信“出票完成”,此时所有系统的数据都对齐了(最终一致性)。
ACID
BASE 实际上是对 ACID 中的 C(一致性)和 I(隔离性)的共同“反叛”,甚至间接影响了 A(原子性)。
Isolation, 隔离性
-
ACID 的 I: 要求事务之间是隔离的。在事务提交之前,外面的世界绝对看不到事务内的中间状态。
- 例子: 我在数据库里把你的余额从 100 改成 0,但我还没 Commit。此时别人查你的余额,必须还是 100。
-
BASE 的 S: 允许“软状态”,意味着允许中间状态可见。
- 例子: 这里的“软状态”往往是因为数据同步延迟造成的。比如主库改了,从库还没改。此时用户读从库,看到了旧数据;或者在分布式事务(如 TCC/Saga)中,第一阶段执行完了,第二阶段还没开始,此时数据处于一个“中间态”(比如预扣减状态),这在严格的 ACID 隔离级别下是不被允许暴露的。
Atomicity, 原子性 —— 范围上的妥协
BASE 为了高可用(BA),在分布式环境下实际上弱化了“即时原子性”。
ACID 的 A: 要么全做,要么全不做,而且是瞬间完成的。用户感觉不到时间差。 BASE 的 BA + E:
- 在微服务架构(基于 BASE)中,一个大的业务操作(比如“下单”)往往被拆成了三个小操作:扣库存、扣钱、生成订单。
- 这三个操作分布在不同机器上。BASE 允许“扣了库存,但钱还没扣”这种状态存在几秒钟(最终一致性)。
- 虽然最终结果也是“要么全成,要么全败(通过补偿机制)”,但在时间轴上,它不再是一个“原子点”,而变成了一段“过程”。
Consistency, 一致性
- ACID 的 C: 强调数据约束和状态即时一致。
- BASE 的 E: 强调最终一致。
分布式一致性算法
一致性算法的目的是保证在分布式系统中:
- 谁说了算(选主 Leader Election)?
- 集群里有 3 个节点,谁是 Primary(主节点)
- 如果主节点挂了,剩下的 2 个节点怎么商量出一个新的主节点?不能两个都抢着当老大(脑裂),也不能两个都谦让没人干活。
- 算法的作用: 像 Raft 或 Paxos 这样的算法,就是一套严密的投票规则,保证在任何乱七八糟的网络环境下,大家能稳稳地选出一个唯一的“老大”。
- 数据一致吗(数据复制 Log Replication)?
- 老大(Primary)收到了写请求,老二和老三(Secondary)收到了吗?
- 如果老大刚写完就挂了,老二还没收到数据,这数据算写入成功吗?
- 算法的作用: 规定了“有多少个节点确认收到,这条数据才算安全”。
务实选择: 最好使用 MongoDB、ZooKeeper、Etcd 这样的现成中间件,不要自己去写代码实现 Paxos 或 Raft 这种一致性算法。
详情请参考分布式一致性算法