分布式系统概述 #
在现代软件工程的演进历程中,从单机应用迈向分布式架构是一个关键的分水岭。这一跨越并非简单的硬件堆叠或代码迁移,而是一场涉及思维模式、设计哲学乃至对物理规律重新认知的深刻变革。
作为全书的开篇,本章将带领读者走出单机系统的舒适区,直面分布式环境下的真实挑战。我们将首先厘清分布式系统的核心定义,剖析其相较于集中式系统的本质差异与优势;随后,我们将重点探讨这一领域中不可回避的技术难题——从不可靠的网络通信到破碎的全局时钟,再到部分失效带来的不确定性。最后,本章将阐述架构师在转型过程中所需完成的心智转变,从追求绝对的确定性转向在一致性与可用性之间寻求平衡的权衡艺术。理解这些基础概念与思维范式,是掌握后续章节中复杂算法与一致性模型的前提,也是构建高可靠、高扩展系统的基石。
什么是分布式系统 #
在展开介绍分布式系统之前,我们先来看分布式系统的对立面:集中式的单机系统(Monolithic)。在单机系统中,所有计算、存储和处理任务都集中在一台计算机或一个中心节点上。单机系统的主要特点有:
- 单一节点:所有功能(如计算、数据存储、用户请求处理)都在一台机器或单一服务器上完成。
- 共享资源:使用共享内存和本地资源,通信速度快,无需网络消息传递。
- 单点控制:所有决策和数据管理由中心节点控制,无需节点间协调。
- 单一时钟:机器上的所有运行进程,使用同一个时钟的时间。
集中式单机系统的优点是:系统简单,请求、计算、存储、处理都在同一台节点上完成,不存在网络延迟、系统时间不一致等分布式系统才会出现的问题,但是缺点也很明显:单一机器节点的故障会导致整个系统的不可用,缺乏容错性;同时性能受限于单台机器的处理能力,只能通过升级硬件(垂直扩展(scale up))来提升性能。
集中式系统的例子有:
- 传统的单机数据库。
- 单服务器的Web应用,处理所有的请求和数据存储。
- 个人电脑运行的本地应用程序。
然而,很多应用场景和要求并不是集中式系统能够解决的,例如:
- 高性能:集中式系统可以通过垂直扩展的方式提升机器的硬件性能(采用更快的CPU、更大的内存等),以提升系统的处理能力。然而,垂直扩展的方式始终无法突破单一机器物理限制的天花板。与之对应的,分布式系统通过*横向扩展(scale out)*的方式,通过将多台机器组合在一起形成一个系统对外提供服务。当发现当前系统扔不满足需求时,可以继续在这个系统上增加机器提升处理能力。例如,在2003年的论文[GFS]中,Google讲述了使用廉价的硬件搭建分布式文件系统的经验。
- 高扩展性:和性能上遇到的瓶颈一样,随着数据量的增大,集中式系统在数据存储上也会遇到瓶颈。而分布式系统通过将数据分布存储在多台机器上,可以在系统无法承载更多数据的时候通过增加节点以提供更大的存储容量。
- 高可用性:现在绝大多数的服务,都要求系统能够保持7x24小时运行,表 system-avaliability 给出了不同的系统可用性需要满足的全年不可用时间。集中式系统中,只有一台机器节点提供服务,这很容易导致*单点故障(single point of failure)*问题:一台机器的故障就导致系统整体不可用。在分布式系统中,通过多台机器构成一个系统,提供了更多的冗余度,在某些机器出现故障时,系统也能够继续运行。
- 在地理位置上离用户更近:在许多情况下,要考虑用户所处的地理位置。例如,一个社交应用,既有来自亚洲的用户,也有来自欧洲的用户,在这种情况下,可能需要分别在不同的大洲建立服务,这些分布于不同地区的服务之间需要协同:例如,用户ID必须是全局唯一的。
- 合规要求:随着全球数据隐私法规的日益严格,为了满足*数据驻留(Data Residency)与数据主权(Data Sovereignty)*的合规性要求,系统架构必须支持跨地域的分布式部署。通过在不同国家或司法管辖区内建立独立的数据中心节点,实现数据的本地化存储与处理,从而确保用户数据严格限制在其所属的地理边界内。
- 资源共享:这里的"资源"不仅指硬件资源,也包括软件资源。例如,在进行大量计算时,为了计算计算流程,需要将计算划分为多个任务,分发到多个机器上同时执行;通过在线共享文档这样的产品,多个用户可以同时浏览、修改文档,无需将文件下载到硬盘上进行修改,同时也不再需要在个人电脑上安装处理软件。
- 业务属性:最后,除去以上集中式系统由于物理限制,要求使用分布式架构以外,某些业务天然就必须满足分布式特性。例如,电商服务中浏览、购买商品在一个平台,但是支付账单要到另外的系统(例如支付平台、银行等)完成,在这种业务中,系统天然就必须是分布式的。
有了和单机系统的横向对比,我们可以来看看分布式系统的定义了。本书采用了在[Coulouris]中提到分布式系统的定义:
分布式系统是指各组件分布于网络互联的计算机上,通过相互传递消息来实现通信并协调运作的系统(A distributed system is a system whose components are located on different networked computers, which communicate and coordinate their actions by passing messages to one another.)。
从上面的定义可以看出:*分布式系统(Distributed System)*是由一组通过网络连接的独立计算机(节点)组成的系统,这些节点协作完成共同任务。每个节点拥有自己的处理器、内存和存储,节点间通过消息传递通信,无共享内存。
组成分布式系统中的每台计算机被称为节点(node)。在这里,“计算机"的概念非常宽泛:可能是个人电脑,也可能是数据中心的服务器、移动设备、联网的各类型IOT设备(如汽车、智能家电等等)。总而言之,“节点"可能是任何联网通信的设备。
分布式系统旨在实现高可用性、可扩展性和容错性,分布式系统通常具有以下特点:
- 多节点协作:分布式系统由多个物理上分散的节点组成来共同工作,这些节点可能位于不同地理位置,也可能是独立的进程。节点之间的地址空间互不关联,因此不会将共享内存的多处理器做为分布式系统的代表。
- 网络通信:节点通过网络(如互联网或局域网)进行通信,网络消息的延迟情况取决于信息链路的具体情况。例如,光缆可能被挖断、网络可能会分区、消息延迟会突然间陡增,这些都是在分布式通信中常见的故障情况。
- 无全局时钟:节点间缺乏统一时间,需通过协议同步。
- 协同工作:组成一个分布式系统中的多个节点,相互通信协同完成一个共同的目标。例如,三个节点组成的存储系统,共同提供强一致的数据服务,在一个节点宕机的情况下,仍然能继续工作;再比如,将一个大的任务划分多个小任务,由多个节点共同执行,最后再汇总得到最后的执行结果。
表 vs-dist-system 和图 introduction/vs 列举了分布式系统和集中式系统的关键特性对比。
| 特性 | 分布式系统 | 集中式系统 |
|---|---|---|
| 架构 | 多节点,节点之间通过网络连接 | 单节点 |
| 通信 | 通过网络进行消息传递,延迟无上限 | 本地调用,延迟低 |
| 扩展性 | 水平扩展 | 垂直扩展 |
| 容错性 | 通过多节点冗余,具备高容错性 | 单点故障就会影响系统的可用性 |
| 一致性 | 需要定义系统允许的一致性模型 | 天然一致,无需额外协调 |
| 复杂性 | 设计和维护复杂 | 设计和维护简单 |
然而,虽然分布式系统相较于集中式系统有不少优点,但是实现一个可靠的分布式系统却并不容易,我们来接着看实现分布式系统的挑战。
分布式系统的挑战 #
开发者最初转向分布式系统开发时,会带着很多单机系统开发时的固有经验。L.Peter Deutsch等人在Sun公司工作期间,从工作中总结出了分布式计算的谬误(fallacies of distributed computing)1:
- 网络是可靠的(The network is reliable)。
- 延迟为零(Latency is zero)。
- 带宽是无限的(Bandwidth is infinite)。
- 网络是安全的(The network is secure)。
- 拓扑结构不会发生改变(Topology doesn’t change)。
- 存在一名管理员(There is one administrator)。
- 传输成本为零(Transport cost is zero)。
- 网络是同构的(The network is homogeneous)。
我们下面详细讨论其中的几个问题。
网络不可靠 #
在单机系统中,消息都是本地调用,延迟低且不会出现不能到达的情况。在单机程序中,两个模块(比如函数A调用函数B)之间的通信是通过内存总线和CPU指令完成的,这保证了单机系统下的通信有以下特点:
- 可靠性:除非硬件损坏,否则不会出现数据丢失这样的异常情况。
- 同步性:这种通信通常是同步的。
- 带宽与延迟:带宽极宽,延迟极低(纳秒级),且非常稳定。
而在分布式系统中,节点之间通过网络进行消息通信的,网络通信很多时候延迟是无上限的:即不能保证数据的延迟在什么范围内,甚至在某些情况下,消息就不会到达。例如,在网络中的消息传递可能出现以下的现象:
- 消息丢失,请求可能在路由器排队时被丢弃了,或者因为光缆被挖掘机挖断了(图 introduction/fail 中的情况(a))。
- 不同的消息以区别于发送时的顺序到达,或者在不同的节点上收到的消息顺序不同。相同的两个节点进行通信时,可能出现先发了消息1,后发了消息2,但因为它们走了不同的路由路径,存在消息2先于消息1到达的可能。
- 由于网络分区等因素,消息一直无法被对端接收,即使接收消息的节点仍然处于工作的状态。
- 由于消息发送失败,会试着重传消息,导致接收节点接收到多个相同消息。
- 在单机中,读取内存的时间是有上限的。但在网络中,一个数据包可能延迟1ms,也可能延迟1分钟,无法区分是接收方宕机(图 introduction/fail 中的情况(b))、处理速度慢、或者是网络问题。
- 消息被接收方成功处理且应答,但是应答消息在回复的过程中丢失(图 introduction/fail 中的情况(c))。
- …
可以看到,以上的故障出现时,表现起来都是消息延迟、乱序、丢失等现象,但是却无法区分到底是"节点故障(如宕机)“还是"网络故障”。
因此,即使采用了像TCP这样封装了出错重试、拥塞控制等逻辑的传输协议,开发者也不能认为网络是可靠的,TCP确实在传输层做了重传和排序,给应用层提供了一种"可靠流"的幻觉。但是:
- TCP无法解决连接断开的问题:如果网线被拔了,TCP重试几次后最终会报错。这时应用层还是得处理失败。
- TCP掩盖了延迟:为了保证顺序,TCP会阻塞后面的数据包,导致应用层感受到剧烈的延迟抖动。
- TCP 无法保证业务层面的"送达”:TCP ACK只是内核告诉内核"我收到了”,不代表应用程序已经处理了该请求。例如可能出现数据包在内核接收到,但是未被应用程序处理之前,应用崩溃的情况。
时钟和顺序问题 #
在人类的直觉里,时间是线性的、绝对的。但在分布式系统中,时间是破碎的。在单机系统中,时间只有一个来源,操作系统就像一个拿着秒表的绝对裁判。例如:
- 进程A在12:00:01写入文件。
- 进程B在12:00:02读取文件。
- 毫无疑问,A发生在B之前。
由于单机系统只有一个全局时钟,可以放心地用物理时间戳来决定事件发生的先后顺序。如果你看到日志里事件X的时间戳比 事件Y小,那么X一定发生在Y之前。在单机世界里,唯一的系统全局时钟为所有事件提供了一个唯一的、严格的序列。
然而在分布式系统中,由于以下因素,不存在全局唯一时钟:
- 不同节点有不同的时间,例如节点A上的时间也许比节点B上的时间走快了一秒,此时就不能根据简单对比不同机器上的时间来来决定事件的先后顺序。
- 消息在节点中通过网络通信进行传输,而网络延迟会出现波动,无法预测消息传递时到底花费了多少时间。
以上的事实导致了在分布式系统中,很难根据节点的物理时间来确定涉及多台机器的事件发生顺序。例如:
- 时钟较快的节点A在10:00:05分发送了一条消息。
- 时钟较慢的节点B收到了这条消息,此时它的本地时间是10:00:04。
- 从日志上看,B在A发出消息之前就收到了消息,这违背了物理定律,节点B放佛收到了一条来自未来的消息。
因此在分布式系统中,如果依赖物理时间戳来解决冲突(比如"谁最后写入谁生效"),这种时钟误差会导致数据的灾难性覆盖。我们将在第 chapter:time 章中深入探讨分布式系统的时间和顺序问题。
部分失效 #
在单机系统中,系统的行为是确定的(Determinism),只会处于两种可能的状态:要么正常工作,要么宕机。这样*“全有或全无(All-or-Nothing)"*的二元状态,相对是好处理的:
- 如果代码逻辑错了,程序会抛出异常或者报错。
- 如果硬件坏了(比如断电),整个操作系统会停止,程序会彻底死掉。
但是在分布式系统中,由多个节点组通过网络通信协调工作,此时面临的是一种全新的不同于单机系统的故障模式:部分失效 (Partial Failure)。部分失效指的是系统中的一部分节点或网络发生了故障,而其他部分依然在正常运行。“部分失效"远比"完全崩溃"难以处理,因为它引入了不确定性 (Nondeterminism)。
想象一下,服务A向服务B发送了一个请求,想要扣减100元余额。在单机函数调用中,这很快就会返回成功或失败。但在分布式网络中,服务A等了5秒钟,什么都没收到。这是存在以下的可能性,且服务A无法区分:
- 请求丢失:请求由于网络原因,根本没到服务B。
- 节点崩溃:请求到了服务B,服务B刚处理一半,甚至刚扣完钱,还没来得及回信,B的电源线被拔了。
- 响应丢失:服务B成功处理了,扣钱成功,发回了"OK”,但这个"OK"的包在回来的路上丢了。
- 处理缓慢:服务B活得好好的,只是因为垃圾回收卡住了,或者数据库锁竞争太激烈,它只是慢,还没来得及回话。
这种"由于不知道发生了什么,所以不知道该怎么办"的状态,就是部分失效带来的最大挑战,我们将在 subsec:failover 中详细解释检测节点失效的相关事项。
部分失效带来的具体问题有:
- “亚健康"状态的进程:故障节点可能处于"半死不活"的状态。它偶尔能响应,偶尔超时,这就导致调用方不敢轻易断开连接,一直重试。
- 数据不一致:如果请求是"从银行账户扣款”,而响应丢失了。发送方以为失败了,于是发起重试;另一种情况下,接收方其实已经扣款成功。如果没有特殊处理,可能导致重复扣款。
- 资源耗尽(级联故障):这是最致命的。假设服务A依赖服务B。服务B发生了部分失效(响应变得极慢)。服务A的线程就会阻塞在等待B的响应上。随着请求源源不断进来,服务A的所有线程都会被挂起等待,导致服务A的内存和CPU被耗尽,最终服务A也挂了。这就像高速公路上的一辆车爆胎减速(部分失效),导致后面整条高速公路堵死(级联故障)。
- 幽灵行为:有时候系统判定某个节点宕机不在线(因为网络拥堵没心跳),于是启动了备用节点。结果源节点并没有宕机,只是由于某些原因导致的响应慢。当网络恢复时,两个节点同时操作同一份数据(脑裂),导致数据损坏。
正是因为这些原因,所以Leslie Lamport曾经戏谑分布式系统为:
分布式系统就是这样一种系统,在这种系统中,一台你甚至不知道存在的计算机的故障会导致你自己的计算机无法使用。(A distributed system is one in which the failure of a computer you didn’t even know existed can render your own computer unusable.)。
虽然这不是一个正式的分布式系统的定义,但是却道出了分布式系统所面临的挑战。简而言之,单机系统出错时很容易知道是系统出了问题,但是在分布式系统中,表现出来的现象是消息超时,但是却无法确切知道发生了什么。
数据一致性 #
如果"部分失效"代表了分布式系统在物理层面的固有缺陷,那么"数据一致性"挑战则构成了其逻辑层面的核心矛盾。为了透彻理解分布式架构的复杂性,有必要先回顾单机模型所提供的理想化环境。在那个世界里,强一致性是系统与生俱来的基础保障:
- 共享内存模型:在多线程程序中,只要加上一个锁,或者使用原子变量,当线程A将变量x修改为100时,线程 B在接下来读取x,一定能读到100。
- 单一数据库:在一个运行在单机上的MySQL中,当事务提交(Commit)成功后,数据就落盘了。无论之后是谁来查询,看到的都是最新数据。
在单机世界里,真理只有一个,而且真理的传播是瞬间完成的。 这里存在一个隐形的"上帝视角”(即操作系统或数据库内核),它保证了所有观察者看到的状态永远是一样的。
但是在分布式系统中,多台机器通过网络消息进行通信时,物理定律(光速限制)打破了这种美好的幻想。为了防止单点故障,我们通常会将数据复制(Replication)到多台机器上(比如主从架构)。一旦有了副本,真理不再是唯一的,真理变成了"流言"。如图 introduction/consistency,想象一下如下的场景:
- 节点A收到了用户的写入请求:“将余额改为100”。
- 节点A更新了自己的数据之后,应答客户端写入成功。
- 节点A通过网络通知节点B:“将数据改成100”。
- 就在这个网络传输的几毫秒(甚至几秒)延迟中,另一个用户向节点B发起了查询请求,但是得到的却是旧的数据。
此时,节点A说余额是100,节点B说余额是0。谁是对的?
- 从时间上看,节点A上的数据是最新的。
- 但从节点B的视角看,它根本不知道A发生了什么。
这就是分布式系统的一致性难题:由于消息通信有延迟,没有别的办法在同一时刻,让所有节点都拥有相同的数据视图。
幸运的是,在分布式系统中,一致性并非黑即白的布尔值(是/否),而是一个频谱(Spectrum)。架构师的工作不再是寻找"完美的真理",而是在"为了数据准确让用户等多久"(延迟/强一致)和"为了速度允许用户看到多少旧数据"(可用性/最终一致)之间,根据业务场景找到那个微妙的平衡点。我们将在 sec:consistency models 中讨论各种一致性模型,在第 chapter:consensus 章中讨论分布式共识算法。
以上就是分布式系统面临的挑战,本书剩余的内容将围绕这些挑战,分别详细讲解如何解决这些问题,表 vs-dist-system-2 做了一个简单的对比和总结,我们将带着这些问题开始本书的讨论。
| 维度 | 单机系统特征 | 分布式系统挑战 | 关键技术/解决方案 |
|---|---|---|---|
| 时间 | 物理时钟全序 | 物理时钟不可靠(section:physical-time) | 逻辑时钟(logic-clock)、TrueTime(subsec:truetime) |
| 故障 | 宕机即停止 | 部分失效 | 副本(第 chapter:Replication 章)、共识算法 (第 chapter:consensus 章) |
| 一致性 | 强一致(ACID) | 副本延迟、数据冲突 | 一致性模型(sec:consistency models)、分布式事务(第 chapter:transaction 章)、共识算法 (第 chapter:consensus 章) |
| 容量 | 单机硬件上限 | 无限数据量 | 分区(第 chapter:partition 章) |
心智的转变 #
我们已经了解了单机和分布式系统的区别,当工程师从单机系统开发转向分布式系统开发时,思维需要进行以下的心智转变:
从"二态逻辑"到"三态逻辑"
在单机系统中,函数调用只有两种可能的结果:要么成功(返回结果)要么失败(抛出异常)。在单机系统中,计算机是可靠的,CPU 指令要么执行了,要么没执行。
在分布式系统中,节点之间的*远程过程调用(RPC)*有三种结果:除了成功和失败以外,还有可能出现消息超时的情况。当出现消息超时时,根本不知道对方是没收到请求,还是处理慢了,还是处理完回复时消息丢了。
因此,分布式工程师必须学会处理这样的"薛定谔状态",在消息通信时,引入*幂等性 (Idempotency)*设计:因为不知道上次有没有成功,系统设计时要允许重试,并且保证重试多次的结果和一次一样。
从"全局一致"到"相对论"
在单机系统中,由于存在全局唯一时钟,因此事件时间存在严格的顺序,例如如果日志显示事件A在10:00:01发生,事件B在 10:00:02发生,那么事件A一定发生在事件B之前。
但是在分布式系统中,不存在全局唯一时钟,因此必须放弃对"绝对时间"的依赖,转而关注事件的"先后顺序"。除此以外,工程师还需要在某些场景下接受最终一致性:这一刻服务A的视角下看是100块钱,服务B的视角下看是90块钱,这在分布式系统中是"正常"的,只要过一会儿我们达成一致就行。
从"完美主义"到"权衡艺术"
在单机系统中,ACID(原子性、一致性、隔离性、持久性)被视为理所当然的黄金标准。数据库承诺:事务只要提交,数据就绝不丢失,且所有人看到的都是最新的状态。开发者习惯了这种"完美"的保障。
然而在分布式世界里,CAP 定理(Consistency, Availability, Partition Tolerance)打破了这种幻想。初学者常误以为 CAP 意味着可以在 C、A、P 三者中任意挑选两个,但在分布式现实中,分区容错性并不是一个选项,而是必须接受的客观事实。网络分区是必然发生的:网络光缆会被挖断,路由器会拥塞,甚至数据中心会断电。一旦网络发生分区(节点之间无法通信),系统就必须面临一个残酷的选择:
- 想要100%的可用性(任何时候都能读写),就必须牺牲强一致性(可能读到旧数据)。
- 想要强一致性(所有人看到的必须一样),就必须在网络分区时牺牲可用性(系统报错或阻塞)。
这要求工程师不再寻求"完美解",而是寻找"最适合业务的解"。在分布式架构中,没有最好的设计,只有在特定约束下最合理的权衡。
我们将在 sec:ACID 中深入讨论ACID,在 section:cap 讨论CAP定理。
从"防御故障"到"拥抱故障"
在单机系统中,故障是异常情况 (Exception),工程师要努力避免系统崩溃。
但是在分布式系统中,出现故障是常态:
- 当系统有1000台机器时,每天都可能有硬盘坏掉,每时每刻都可能有网络抖动。
- 要特别当心部分失效问题:整个系统没挂,但因为某个非核心服务响应慢,拖垮了整个线程池,导致主站瘫痪。
这要求工程师写代码时默认依赖的服务会出现宕机、消息响应延迟、数据乱序等情况。学会使用熔断 (Circuit Breaker)、降级 (Degradation)、隔离 (Bulkhead)等技术手段,与其让系统硬撑着死掉,不如优雅地切断坏死部分,保全主体。
本章小结 #
本章作为全书的开篇,正式推开了分布式系统的大门。我们从最基础的定义出发,通过与传统集中式单机系统的对比,确立了分布式系统的基本认知,并剖析了其背后所面临的复杂挑战与思维范式的转变。
- 架构演进:从单机到分布式。单机系统是基于共享内存和单一时钟,具备模型简单、强一致性等优点,但面临单点故障和垂直扩展(Scale Up)的物理瓶颈。分布式系统是由一组通过网络互联的独立计算机(节点)组成的系统,它们通过消息传递进行通信并协调运作。为了突破单机的限制,我们转向了分布式架构。通过横向扩展(Scale Out),利用多台机器协作,实现了高性能、高扩展性、高可用性以及满足数据合规和业务隔离的需求。
- 核心挑战:美好的假象被打破。分布式系统虽然强大,但它不是免费的午餐。我们打破了"网络是可靠的"、“延迟为零"等常见的误区(分布式计算的谬误),并直面了以下几大核心挑战:
- 网络不可靠:与单机可靠的内存总线不同,网络通信存在丢包、乱序、延迟无上限等问题。即使是 TCP协议也无法解决业务层面的"送达"确认或连接断开后的逻辑处理 。
- 时钟与顺序:分布式系统缺乏全局唯一的物理时钟,导致我们无法单纯依赖物理时间戳来判定分布式事件的先后顺序,必须寻找新的逻辑时钟机制。
- 部分失效:这是分布式系统最棘手的问题。不同于单机系统"全有或全无"的确切状态,分布式系统存在"僵尸"状态——无法确定对方是死是活,也无法确定请求是否执行成功。
- 数据一致性:由于数据副本的存在和光速的物理限制,要在同一时刻让所有节点拥有相同的数据视图极其困难。我们需要在"强一致性(等待同步)“和"可用性(快速响应)“之间寻找平衡。
- 心智转变:工程师的自我进化。从开发单机应用转向分布式系统,不仅仅是技术的升级,更是思维模式的重构 :
- 从二态到三态:放弃"成功/失败"的二元论,学会处理"成功/失败/超时(未知)“的三态逻辑,并引入幂等性设计。
- 从绝对到相对:放弃对全局绝对时间的依赖,转而关注事件的因果顺序和逻辑时钟 。
- 从完美到权衡:接受 CAP 定理的约束,不再寻求完美的强一致性,而是根据业务场景在一致性与可用性之间做权衡。
- 从防御到拥抱故障:承认故障是常态,通过熔断、降级等手段设计具备容错能力的系统,而非祈祷故障不发生。
通过本章的学习,对分布式系统有了初步的了解,知道了分布式系统的挑战和难点。在接下来的章节中,我们将针对本章提出的复制、分区、逻辑时钟、共识算法、一致性模型等问题,逐一给出具体的工程解决方案和理论依据。