分布式系统:容错与复制


在分布式系统中,复制是提供高可用性与容错能力的关键技术,同时容错在安全关键系统及其他重要系统中也常作为必备服务。

复制的作用

数据复制指在多个计算机中维护数据副本的技术,它通过提升性能、提供高可用性和增强容错能力,成为保障分布式系统有效性的关键。复制技术应用广泛,例如 Web 资源在浏览器和代理服务器上的缓存就是一种复制形式,DNS 服务也依赖于在互联网中维护名字-属性映射的副本来实现日常访问。复制的主要作用包括:

  • 增强性能:数据缓存是最常见的手段,如 Web 缓存可减少访问延迟,更复杂的系统则采用负载均衡以实现高性能服务。对于不变数据,复制非常简单且开销小;对于变化数据,则需要额外协议来保证客户端能获得最新数据。
  • 提高可用性:复制能通过在多个独立服务器上保存副本来提升可用性,假设每个服务器故障或不可访问的概率为 \(p\),则所有 \(n\) 个副本同时不可用的概率为 \(p^n\),从而整体可用性提升为 \(1-p^n\)。例如若两个服务器各自故障概率为 5%,则系统可用性可达 99.75%。相比之下缓存未必保存全部数据,因此不一定能保证应用层面的高可用性。
  • 实现容错:容错服务要求在特定故障范围内始终保持严格正确的行为,复制技术同样可用于实现容错:理论上,\(f+1\) 个副本可容忍最多 \(f\) 个崩溃故障;对于拜占庭故障,则需要 \(2f+1\) 个副本来通过多数决机制保证正确响应。但实现容错远比理论复杂,系统必须精确管理组件间的协调,以应对随时可能发生的故障。

复制透明性是常见的需求,即客户端无需感知多个物理副本的存在,只对逻辑对象执行操作并期望返回单一结果。另一个关键需求是一致性,它关注针对复制对象的操作是否产生符合正确性要求的结果。在某些场景(如断链操作)下,可以暂时容忍不一致;但在多数情况下,若不同客户端通过不同副本获得不一致的数据,则会破坏应用的正确性标准,这是不可接受的。

系统模型与组通信的作用

系统中的数据由一系列对象组成,每个逻辑对象通过一组称为副本的物理拷贝来实现。副本是存储在特定计算机上的物理对象,它们的数据与行为在系统操作下需满足某种程度的一致性要求。同一对象的不同副本在任意给定时刻未必完全相同,某些副本可能已收到更新,而其他副本可能尚未同步。副本管理器(Replica Manager, RM)是负责管理副本的组件,它在所在节点上直接对副本执行操作。在客户端-服务器架构中,副本管理器通常位于服务器;而在某些集成场景中,同一个应用进程可以同时作为客户端和副本管理器。
副本管理器一般需要保证对副本的操作可恢复,即操作即使中途失败也不会导致副本进入不一致的状态。某些情况下需要进一步假设每个管理器是一个状态机,这意味着它对副本执行的操作是原子的,且所有操作的执行效果等同于以某种确定的顺序依次执行。同时,副本的状态完全由其初始状态和执行的操作序列决定,不受外部非确定性因素(例如时钟读数、传感器数据等)影响。这一假设是保证不同副本管理器之间能够最终达成一致的基础,也要求系统必须严格控制操作的执行顺序,并通常意味着服务器不能采用多线程架构。
一般情况下,每个副本管理器会为每个对象维护一个副本,但不同对象的副本可能分布在不同的副本管理器上,例如根据用户的访问模式进行分布。副本管理器的集合可以是静态的或动态的:

副本管理器集合 说明
静态集合 管理器成员固定不变
动态集合 允许管理器加入或退出,例如因节点崩溃、恢复或新设备加入而发生变化

下图展示了副本管理的一般模型:管理器集合共同为客户提供服务,客户通过访问逻辑对象(如日记或银行账户)来发出请求。客户每次请求可能包含对单个或多个对象的一系列操作,其中不含更新的称为只读请求,包含更新的称为更新请求。客户的请求首先由前端(Front End,FE)处理,它负责通过消息传递与多个副本管理器通信,这是实现复制透明性的关键。前端可实现在客户进程地址空间内,也可作为独立进程存在。
image

在复制对象上执行一个操作通常涉及五个阶段,不同系统对各阶段的实现与排序可能不同:

  1. 请求:前端将一个或多个副本管理器分发请求,可以是与单个管理器通信并由其转发,也可以是前端直接组播至所有管理器。
  2. 协调:副本管理器相互协调,以确保请求执行的一致性。此阶段它们可能就需要执行请求达成共识,并确定该请求相对于其他请求的排序,排序类型包括:FIFO 序、因果序、全序。
  3. 执行:副本管理器执行请求,可能以试探性方式执行,即效果可回滚。
  4. 协定:副本管理器就请求执行的结果达成一致,例如在事务中共同决定提交或放弃事务。
  5. 响应:一个或多个副本管理器向前端返回应答。不同系统设计不同:有些仅一个管理器响应,有些则由前端接收多个应答后选择或综合为一个返回给客户。例如,高可用性系统可能将最先到达的应答返回;容忍拜占庭故障的系统则可能需要依据多数管理器的应答来决定。

各系统可根据其目标(如支持断链操作或实现容错)安排各阶段的顺序与内容,例如:

  • 支持断链操作的系统:尽早向客户返回响应;
  • 容错系统:确保结果正确后才可应答。

组通信的作用

组通信在管理复制数据中是核心角色。复制等实际场景中进程可在系统运行期间加入或离开组,例如用户可以动态增删副本管理器,副本管理器也可能因崩溃而从操作集合中被移除。为适应进程加入、离开与崩溃,系统通常需要如故障检测与组成员关系变更通知的高级特性。

组视图

组成员关系服务需要维护组视图,即当前组成员的列表,通常按加入顺序来排列。每当有进程加入或离开组,就会产生一个新的组视图。值得注意的是,组成员关系服务可能因“怀疑”某个进程而将其删除,即使该进程并未真正崩溃,例如由于通信故障导致其不可达。被删除的进程将不再接收组内消息,若错误地怀疑并删除进程,会降低组的整体效能。因此设计时需尽可能提高故障检测的准确性,并确保即使发生误判,组通信仍能正常工作。
组管理服务还需处理网络分区问题,根据其设计可分为主分区型与可分区型:

组管理服务类型 说明 适用场景
主分区型 在网络分区发生时,最多允许一个子组(通常是较大的子组)继续运行,其他进程则被挂起 数据一致性至关重要、分区不一致代价高于断链操作收益的场景
可分区型 允许多个子组在分区期间继续独立操作 可接受临时不一致的应用,例如音视频会议中各组可独立讨论,分区恢复后再合并结果

视图传递

每当组成员变化时,如果程序员不能在本地就决定如何响应变更,而不得不查询所有其他的组成员的状态是非常不灵活的。视图传递是组管理服务的关键机制,每当组成员关系变化时,组管理服务会向组内每个进程 p 传递一系列视图 v₀(g), v₁(g), v₂(g), …。系统需保证视图传递满足如下表所示的基本需求,这些条件提供了视图变化的一致性和可预测性:

视图传递要求 说明
顺序 如果进程 p 传递了视图 vᵢ(g),随后传递 vⱼ(g),则不存在进程 q 在 vᵢ(g) 之前传递 vⱼ(g)。
完整性 如果进程 p 传递了视图 v(g),则 p ∈ v(g)。
非平凡性 若进程 q 加入组后与进程 p 保持长期可达,则最终 q 会出现在 p 传递的视图中;若发生持久分区,各分区传递的视图最终将不包含其他分区中的进程。

视图同步的组通信

视图同步的组通信进一步扩展了可靠组播语义,以适应动态组视图,并保证:

视图同步的组通信保证 说明
协定 正确的进程传递相同的视图序列,且在任一视图中传递相同的消息集合。
完整性 正确的进程不会重复传递消息;若进程 p 传递消息 m,则发送 m 的进程处于 p 传递 m 时的视图中。
有效性(封闭组) 正确进程总能传递它们发送的消息。若系统在向进程 q 传递消息时发生故障,则会向剩余进程传递一个删除了 q 的新视图。

视图同步机制在概念上于系统中划出一条界线,所有被传递的消息都一致地位于界线某一侧。这使得程序员在传递新视图时,可根据本地事件次序推断其他正确进程已传递的消息集合。考虑一个由 p, q, r 组成的组,如下图所示,假设 p 在视图 (p, q, r) 中发送消息 m 后崩溃:

  • 若 m 在到达其他进程前 p 已崩溃,则 q 和 r 将传递新视图 (q, r),且均不传递 m(a)。
  • 若 m 至少到达 q 或 r 之一,则 q 和 r 均先传递 m,后传递新视图 (q, r)(b)。
  • 不允许先传递新视图再传递 m(c),因为此时它们将传递已经出现故障的进程的消息。
  • 不允许不同进程以相反次序传递消息与视图(d)。

image

状态转移

视图同步通信特别适用于状态转移场景,其典型任务是将工作状态从当前组成员迁移到新加入的成员。以管理日记状态的副本管理器为例:当一个新的副本管理器加入组时,它需要获得日记的完整最新状态,并且必须确保既不遗漏在获取状态期间发生的任何更新,也不会重复应用已经包含在该状态中的更新。利用视图同步通信,一种简单的实现方案如下:

  1. 视图变更与状态捕获:当组内传递第一个包含新进程的视图时,现有成员中的一个指定进程(例如最早加入的成员)立即捕获当前组状态,并通过一对一通信将状态发送给新成员,然后暂停自身执行。与此同时,其他现有成员也同步暂停执行。
  2. 状态同步的保证:根据视图同步通信的语义,在状态被捕获的那一时刻,所有已应用到该状态的更新操作在组内各成员间是明确且一致的,因此新成员获得的状态具有确定性。
  3. 恢复执行:新成员在接收并集成该状态后,向整个组播发一条“开始”消息。至此,所有进程(包括原有成员和新成员)基于统一的新状态恢复执行。

容错服务

容错服务通过复制数据与功能来构建,使其在至多 f 个进程故障时仍能提供正确服务。假设通信可靠且不发生网络分区,每个副本管理器在未崩溃时应遵循所管理对象的语义规约(例如,银行账户的规约须保证转账资金不丢失、仅存款与取款能改变余额等)。
一个复制服务若要被视为正确的,应满足以下条件:在发生故障时,服务仍然能够响应客户端请求,且客户端无法区分该服务是基于多个副本实现,还是由单个正确的副本管理器单独提供的。然而若不进行细致的设计与协调,即便仅针对单个操作(而非事务)的场景,系统仍可能出现违反预期的异常行为。
例如,考虑一个简单的复制系统:副本管理器 A 和 B 分别维护银行账户 x 和 y 的副本。客户先在本地副本管理器上读写,若本地管理器故障则转向另一个,更新操作在后台异步传播。假设账户初始余额均为 0,客户 1 在管理器 B 上设置 x = 1,随后试图在设置 y = 2 时发现 B 故障,转而向管理器 A 更新 y。客户 2 则在管理器 A 上读取,先读到 y = 2,再读到 x = 0,这是因为 B 的故障导致对 x 的更新尚未传播至 A。按发生顺序排列的操作序列如下,序号表示首次执行该操作的计算机:

  • 客户1:setBalance_B(x, 1) → setBalance_A(y, 2)
  • 客户2:getBalance_A(y) → 2 → getBalance_A(x) → 0

该执行违反了银行账户的规约:若客户 2 读到 y = 2(即在 x 更新之后发生的操作),则应读到 x = 1。若账户由单一服务器实现则异常不会发生,因此必须定义复制系统的正确行为标准,以避免此类问题。

线性化与顺序一致性

复制对象有多种正确性准则,最严格的是线性化,单一服务器上的单副本对象总能将客户操作串行化。通常依据一个虚拟的客户操作交错序列来定义复制对象的正确性准则,该序列不一定在任一具体副本管理器上发生,但用于判定执行是否正确。若一个复制共享对象服务满足以下条件,则它是可线性化的。每个客户操作都对应于共享对象的一个虚拟规范映像,客户所见视图应与该映像一致,且操作结果仅当其在该交错序列中出现时才有意义。因为不存在任何满足账户规约的操作交错序列,前述银行账户的例子不满足线性化。

  1. 存在一个全体客户操作的交错序列,符合对象(单个)正确副本的规约;
  2. 该交错序列中操作的次序与实际运行中的实时次序一致。

线性化的实时性要求符合“客户应看到最新数据”的预期,但也实际情况下时钟难以精确同步。顺序一致性是一种较弱的正确性条件,它保留了线性化的第一个条件,但放宽了第二个条件。若一个复制共享对象服务满足以下条件,则它是顺序一致的。顺序一致性不要求实时全序,仅要求保持每个客户的操作程序次序。操作交错可以任意重排不同客户的操作,只要不违反各客户自身的顺序且结果符合对象规约即可。

  1. 存在一个全体客户操作的交错序列,符合对象(单个)正确副本的规约。
  2. 该交错序列中操作的次序与每个客户程序中的执行次序一致。

每个可线性化服务必然是顺序一致的,因为实时次序已隐含了程序次序,但顺序一致的服务却不一定满足线性化。例如以下执行是顺序一致的,但不可线性化:

  • 客户1:setBalance_B(x, 1) → setBalance_A(y, 2)
  • 客户2:getBalance_A(y) → 2 → getBalance_A(x) → 0

这种情况可能发生在客户 1 对 x 的更新尚未传播到副本管理器 A 时,客户 2 就已经读取了 x 的值(即使 A 和 B 都没有发生故障)。由于 getBalance(x) → 0 实际发生在 setBalance(x, 1) 之后,这违反了线性化对实时次序的要求。尽管如此,以下操作交错序列仍满足顺序一致性的条件:getBalance_A(y) → 0, getBalance_A(x) → 0, setBalance_B(x, 1), setBalance_A(y, 2)。该序列既保持了每个客户自身操作的先后顺序,也符合对象语义规定,因此是顺序一致的,但其中操作的整体排列与实际发生的时间次序不符,故不满足线性化。

被动(主备份)复制

用于容错的被动复制(主备份复制)模型如下图所示,该模型的系统中始终存在一个主副本管理器和若干个备份副本管理器(从管理器)。该模型的核心是前端仅与主副本管理器通信,主副本管理器执行操作,并将更新后的状态发送给各个备份副本管理器。若主副本管理器发生故障,则某个备份副本管理器会被提升为新主副本管理器。
image
当客户端需要执行操作时,流程依次如下:

  1. 请求:前端将请求(包含唯一标识符)发送至主副本管理器。
  2. 协调:主副本管理器按接收顺序原子地执行每个请求。它会检查请求的唯一标识符,若该请求已执行过,则直接重新发送响应。
  3. 执行:主副本管理器执行请求,并存储响应结果。
  4. 协定:若请求为更新操作,主副本管理器将更新后的状态、响应及唯一标识符发送给每个备份副本管理器;各备份副本管理器确认收到。
  5. 响应:主副本管理器将响应返回给前端,前端再转发给客户端。

在主副本管理器正常运行时,由于所有操作均由主副本管理器在共享对象上顺序执行,系统显然满足线性化。当主副本管理器故障时,若满足以下两个条件,系统仍能保持线性化:

线性化条件 说明
唯一接替 有且仅有一个备份副本管理器被提升为新主副本管理器(若多个备份同时接管,系统将无法正确执行)
状态一致 在接管时刻,存活的备份副本管理器就哪些操作已执行达成一致

这两个条件可通过视图同步组通信实现,将副本管理器(主与备份)组织为一个组,主副本管理器使用视图同步组播向备份发送更新。当原主崩溃时,通信系统最终向存活的备份传递一个不包含原主的新视图。新主可通过预定规则(如选择视图中的首个成员)确定,并在名字服务中注册,供前端查询。视图同步的排序机制与重复请求标识符检测相结合,可以保证:在传递新视图之前,所有备份副本要么都完成了某次更新的传递,要么都未进行该次传递。
若前端未收到响应,会将请求重传至接管后的新主副本管理器。主副本管理器可能在执行的任何阶段崩溃,如下所示。但是是新主副本管理器无需确切知道原主崩溃时所处的阶段,当收到请求时它直接从协调阶段(2)开始执行即可。借助视图同步,新主也无需询问备份副本管理器,因为它们已收到相同的消息集合。

  • 若在协定阶段(4)之前崩溃,存活的副本管理器将不再处理该请求。
  • 若在协定阶段中崩溃,它们可能已执行该请求。
  • 若在协定阶段之后崩溃,它们肯定不会再处理该请求。

被动复制模型适用于主副本管理器以非确定性方式运行(例如采用多线程)的场景,由于主副本管理器向备份发送的是更新后的状态而非操作本身,备份只需被动记录这些由主副本管理器独立决定的状态。为容忍至多 f 个进程崩溃,系统需要 f+1 个副本管理器(但无法容忍拜占庭故障)。前端无需具备容错能力,但需能在当前主副本管理器无响应时发现并转向新主。
被动复制的主要缺点是开销较大,视图同步通信在每次组播时需要多轮交互,且在主副本管理器故障时,组通信系统需协商并传递新视图引入额外延迟。一种变体允许客户端将读请求提交至备份副本管理器,以减轻主副本管理器的负载,此类系统不保证线性化但仍能提供顺序一致性的服务。

主动复制

用于容错的主动复制模型如下图所示,此时所有副本管理器以对等角色组成一个组,每个管理器都是一个状态机。前端通过全序、可靠的组播原语将请求消息发送到副本管理器组,每个副本管理器独立且以相同的方式处理请求并产生响应。即使部分副本管理器崩溃,剩余的管理器仍能继续正常响应。主动复制还能容忍拜占庭故障,因为前端可以收集并比较来自不同管理器的响应。
image
当客户端请求一个操作时,事件顺序如下:

  1. 请求:前端为请求附加唯一标识符,并使用全序可靠的组播原语将其发送给副本管理器组。前端在收到响应前不会发送新请求。
  2. 协调:组通信系统以相同的全序将请求传递给每个正确的副本管理器。
  3. 执行:每个副本管理器执行请求。由于它们都是状态机且接收到的请求顺序一致,因此正确的管理器会以相同的方式处理请求,并生成包含请求标识符的响应。
  4. 协定:由于组播的传递语义已保证顺序和可靠性,此阶段不需要额外协调。
  5. 响应:每个副本管理器将响应发送回前端。前端根据故障模型和组播算法决定如何处理响应。例如,若仅需容忍崩溃故障且组播满足统一协定和排序特性,前端可将第一个到达的响应返回给客户端,并丢弃其余响应(通过标识符区分不同请求的响应)。

主动复制的一致性保证如下。实现可靠全序组播等价于解决共识问题,这要求系统是同步的,或采用故障检测器等技术绕过异步系统不可能性结论。某些共识算法可容忍拜占庭故障,基于此类算法的主动复制系统可在包含至少 2f+1 个副本管理器时屏蔽至多 f 个拜占庭故障。前端需等待收集到 f+1 个相同的响应(经数字签名验证)才返回给用户,并丢弃其余响应。

主动复制的一致性 说明
顺序一致性 所有正确的副本管理器以相同的全序处理请求集合。组播的可靠性确保它们收到相同的请求集,全序保证执行顺序一致。由于管理器是状态机,每个请求后它们会达到相同的状态。前端以 FIFO 顺序发送请求,因此系统满足顺序一致性。
线性化 主动复制系统不保证线性化,因为副本管理器处理请求的全序可能与客户端发出请求的实际时间顺序不一致。在时钟大致同步的系统中,可通过为请求附加时间戳来近似实现实时顺序,但由于时间戳的不精确性,只能保证“大致一致”而非严格线性化。

在实际操作中并非所有操作都需要严格排序,例如两个只读操作或更新不同对象的写操作是可交换的——无论以何种顺序执行,最终状态都相同。利用这一特性,系统可避免对所有请求进行全局排序的代价,采用应用特定的组播排序语义。前端可将只读请求直接发送给单个副本管理器,虽然这会牺牲与组播相关的容错性,但服务仍保持顺序一致。若该管理器故障,前端可简单地将请求重定向到另一个管理器,从而维持可用性。

复制数据上的事务

前面的客户端每次仅对复制对象集合发起单个操作,在事务系统中对象可以通过复制来提高可用性和性能,但对客户端而言,操作复制对象的事务应与非复制对象的表现一致。在无复制系统中,事务以某种顺序执行一次,通过交错执行的串行等价性来保证正确性。对于复制对象上的事务,应产生与在单一对象集上执行一次相同的效果,这一性质称为单拷贝串行化。与顺序一致性不同在于它仅考虑单个操作的有效交错,不涉及将客户操作组合为事务的情况。每个副本管理器为其管理的对象提供并发控制与恢复机制,例如使用两阶段加锁。某个副本管理器发生故障时的恢复过程更为复杂,故障管理器在恢复后必须从其他管理器获取信息,以更新其对象到最新状态。

复制事务的体系结构

前面的系统中,前端可将客户端请求组播到副本管理器组,或发送给某个特定的副本管理器由其负责处理并响应。此处假设前端将客户端请求发送到逻辑对象的副本管理器组中的某一个成员,有两种实现方法:

复制事务方法 说明
主拷贝方法 所有前端与一个主副本管理器通信以执行操作,并由该管理器负责更新备份。
任意管理器方法 前端可与任一副本管理器通信执行操作,但此时收到操作请求的副本管理器需协调组内拥有该对象副本的其他管理器。

不同复制方案对成功完成操作所需的副本管理器数量有不同规则。例如读一个/写所有方案的 read 请求可由单个副本管理器执行,而 write 请求必须由组内所有副本管理器执行。另一个关键问题是更新传播的策略,有如下 2 种:

特性 惰性更新传播 及时更新传播
基本思想 与前端连接的副本管理器暂不将更新请求转发给其他管理器,直到事务提交时才统一传播。 在事务提交前,每个更新请求立即转发给所有副本管理器。
通信开销 较低,仅在提交时进行批量通信 较高,每个更新操作都需实时同步到所有管理器
并发控制复杂度 较高,需要协调多副本间的执行顺序,易出现不一致。 较低,更新即时同步,利于保持多副本一致性。
主要挑战 各副本管理器可能不了解其他管理器的执行状态,若多个事务同时更新不同副本,易导致状态不一致。 通信延迟可能影响事务响应时间,尤其在副本数量多或网络状况差时。
与事务协调的关系 事务的提交阶段需包含向所有副本同步更新,若同步失败则可能导致部分副本不一致。 更新在事务执行过程中已同步,提交阶段仅需最终确认,一致性更容易保证。

两阶段提交协议在复制环境中扩展为两层嵌套的两阶段提交:协调者与参与者通信,而若协调者或参与者本身是副本管理器,则它还与其他副本管理器通信。简要来说:

  • 第一阶段:协调者向参与者发送是否能提交,参与者再将此消息传递给其他副本管理器,并在回答协调者前收集它们的响应。
  • 第二阶段:协调者发送提交或放弃请求,该请求被传递给副本管理器组的成员。

主拷贝复制可用于事务环境,此时所有客户端请求(无论是否只读)直接发送到主副本管理器,并发控制在主副本管理器上实施。当事务提交时,主副本管理器与备份通信,然后使用及时方法响应用户。这种形式的复制允许在主副本管理器故障时,由某个备份一致地接管。若采用惰性方法,主副本管理器在更新备份前就响应前端,此时接替故障主管理器的备份可能不具备数据库的最新状态。

读一个/写所有方案通过在每个副本管理器上使用两阶段加锁来实现单拷贝串行化,且前端可与任一副本管理器通信。

  • 每个 write 操作必须在所有副本管理器上执行,管理器在受影响对象上加写锁。
  • 每个 read 操作由单个副本管理器执行,该管理器在受影响对象上加读锁。

由于任何两个 write 操作会在所有副本管理器上请求冲突锁,而 read 操作与 write 操作会在单个副本管理器上请求冲突锁,因此最终可实现单拷贝串行化。
image

可用拷贝复制

简单的读一个/写所有复制在实际中难以适用,因为当副本管理器因崩溃或通信故障不可用时该方案将无法执行。可用拷贝复制方案允许部分副本管理器暂时不可用,此时客户端在正常情况下,一个工作的副本管理器接收并执行客户端请求:

  • read 请求由接收到请求的副本管理器直接执行。
  • write 请求由接收到请求的副本管理器及组内其他可用副本管理器共同执行。

例如在下图中事务 T 的 getBalance 操作由管理器 X 执行,而其 deposit 操作则由管理器 M、N 和 P 执行。每个副本管理器上的本地并发控制作用于其本地执行的操作,例如在 X 上事务 T 已经读取了对象 A,因此事务 U 的 deposit 操作在 T 完成前不能更新 A。只要可用副本管理器集合保持不变,本地并发控制就能保证单拷贝串行化。然而,如果在相互冲突的事务执行过程中发生副本管理器故障或恢复,单拷贝串行化就可能被破坏。
image

在可用拷贝复制中,每个副本管理器可能因故障暂时不可用。假设故障属于良性崩溃:崩溃的管理器会被一个新进程替代,该进程通过恢复文件将其管理的对象恢复到最近已提交的状态。前端通过超时机制来判断副本管理器当前是否可用:

  • 如果请求发送给一个已崩溃的管理器,前端在超时后会重新尝试将请求发送给组内的其他副本管理器;
  • 如果请求被某个管理器接收,但该管理器尚未完成恢复、数据仍处于过时状态,它会拒绝该请求,前端则会再次尝试其他管理器。

为了维持单拷贝串行化,故障和恢复事件在事务的视角下必须是可串行化的。也就是说,事务可以根据自己能否访问某个对象,推断出该对象对应的副本管理器故障是发生在这个事务完成之后,还是开始之前。如果不同的并发事务观察到了互相矛盾的故障情况(例如,事务 T 认为故障发生在自己完成之后,而事务 U 认为同一个故障发生在自己开始之前),那么系统就无法保证单拷贝串行化的正确性。

虽然 write 操作直接作用于所有可用副本,本地并发控制能保证同一对象上的冲突写是可串行化的。但一个事务的 read 操作与另一事务的 write 操作未必作用于同一副本,因此可能形成读写依赖环。为避免此类情况,需要使用本地验证机制确保故障与恢复对事务而言是串行化的。该额外机制在事务提交前检查其所访问过的副本管理器是否发生故障(或恢复),当事务发现故障时会尝试与故障管理器通信以确认其尚未恢复。验证的其余部分可整合到两阶段提交协议中,例如检查访问对象期间是否有管理器故障。

网络分区

复制方案必须考虑网络分区,该情况会将副本管理器组分割为两个或多个子组,子组内成员可相互通信但子组之间无法通信。复制方案通常假设分区最终会被修复,因此每个分区中的副本管理器必须确保:在分区期间执行的任何操作,在分区修复后不会导致数据不一致。
image
网络分区的处理方法主要可分为两类:

网络分区处理方法 说明
乐观方法 在分区期间不限制可用性,允许所有分区进行更新。这可能导致不同分区产生不一致,必须在分区修复后通过验证与协调来解决。
悲观方法 即使在无分区时也对可用性有所限制,但在分区期间可防止不一致的发生。

可用拷贝算法可以在每个分区内独立使用,即使在分区期间读操作的可用性仍可保持正常水平。当分区修复后,需对发生在不同分区中的可能冲突的事务进行验证。若验证失败,必须采取措施解决不一致性:

  • 若未发生分区:冲突的事务之一可通过延迟或放弃来解决;
  • 分区存在时:冲突的事务可能已在不同分区中提交,此时唯一的选择是放弃其中一个事务,甚至需要对对象状态进行补偿操作。

可以使用前驱图进行冲突检测,此时每个分区维护受事务读写操作影响的对象日志用于构建前驱图。图中结点表示事务,边表示事务读写操作之间的冲突。由于分区内已实施并发控制,各分区的前驱图应无环。验证过程将不同分区的前驱图合并,并在跨分区事务之间添加代表冲突的边,若最终图中出现环则验证失败。

法定数共识

法定数共识是用于阻止分区期间不同分区的事务产生不一致的方法,其核心思路是只允许在一个分区中执行操作。由于分区间无法通信,每个分区中的副本管理器子组需独立判断自己是否具备执行操作的资格。法定数是指一个拥有足够“权利”执行操作的副本管理器子组,常用标准是“多数派原则”:如果一个子组包含大多数成员,它即可构成法定组,其他子组则因不占多数而无法执行操作。对逻辑对象的更新操作只需在一个满足条件的副本管理器子组上成功执行即可,组内其他成员持有的过时副本可通过版本号或时间戳来判断是否最新。
Gifford 提出了一种加权投票的法定数方案,将一定数量的选票分配给逻辑文件的每个物理副本,用于反映该副本的重要性或可靠性。每个 read 操作必须获得至少 R 张选票的读法定数,每个 write 操作必须获得至少 W 张选票的写法定数。R 与 W 需满足以下条件:

\[W > \text{总选票数的一半} \quad \text{且} \quad R + W > \text{总选票数} \]

这些条件保证任意一对读法定数与写法定数(或两个写法定数)必定包含至少一个共同的副本,从而在网络分区时,不可能在不同分区中对同一副本执行冲突操作。操作执行流程如下:

  • read 操作:首先通过查询版本号收集至少 R 张选票的读法定数(不要求所有副本均为最新)。由于读法定数与写法定数必有重叠,该集合中至少包含一个最新副本,随后可在任一最新副本上执行读取。
  • write 操作:需收集至少 W 张选票的写法定数,且法定数中所有副本必须是最新的。若最新副本数不足,需先将某个旧副本替换为最新副本以满足条件。接着在写法定数包含的所有副本管理器上执行更新,递增所有对象副本的版本号,并向客户端报告完成。剩余可用副本管理器可通过后台任务异步更新。

该方案的并发控制可结合两阶段读写锁实现,由于法定数的重叠特性,这些锁可共同保证单拷贝串行化。

法定数加锁 说明
读操作 对联系到的副本管理器设置读锁
写操作 对涉及的副本管理器设置写锁,锁的粒度与版本号相同

加权投票算法的一个优势是允许通过配置选票来平衡性能与可靠性,例如降低 W 可提高写操作的可靠性与性能,降低 R 可提高读操作的可靠性与性能。该算法还支持弱代表副本,它为其分配 0 张选票,确保它们不会进入任何法定数。一旦获得读法定数,读操作可在任意最新副本上执行;若本地弱代表副本恰好是最新的,读取可直接在本地完成,从而加速读操作。法定数共识方法的主要缺点是,需要从 R 个副本管理器中收集一个读法定数,因此 read 操作的性能被降低了。

虚拟分区算法

法定数共识能在网络分区时保证正确性,可用拷贝则能降低读操作的开销,虚拟分区算法旨在结合法定数共识与可用拷贝两种方法的优势。虚拟分区是真实物理分区的抽象表示,是由一组能相互通信的副本管理器构成的逻辑集合,类似于组视图。
如果一个虚拟分区包含足够多的副本管理器,使得该分区能够形成对某对象的读法定数与写法定数,则事务可在该虚拟分区内操作。此时,事务采用可用拷贝算法执行:读操作只需访问该对象的单个副本(可选择“最近”的副本),写操作则在分区内所有可用副本上执行。如果在事务执行期间虚拟分区成员发生变化,如副本管理器故障或恢复时,该事务将被中止。由于所有存活的事务以相同顺序感知副本管理器的故障与恢复,从而保证了单拷贝串行化。

当一个虚拟分区的成员检测到无法访问其他成员时(例如写操作未获确认),它会尝试创建一个具有读写法定数的新虚拟分区。以 4 个副本管理器 V、X、Y、Z 为例,每个管理器持有一张选票,读写法定数要求为 R=2、W=3。初始时所有管理器相互连接,它们采用可用拷贝算法执行事务。例如,一个由读操作后接写操作组成的事务 T,其读操作可在某个管理器(如 V)上执行,写操作则在全部 4 个管理器上执行。假设在 T 执行读操作时,V 仍与 X、Y、Z 相连,随后发生如下图所示的网络分区(V、X 分在同一侧,Y、Z 各自隔离)。
image

当 T 尝试执行写操作时,V 会发现自己无法连接 Y 和 Z。此时,V 将不断尝试连接 Y 和 Z,直到能与其中至少一个重新通信。若 V、X、Y 能相互通信且形成足够的读写法定数,它们便构成一个新的虚拟分区。若事务在执行期间遇到虚拟分区重组,则该事务必须中止。此外,新虚拟分区内的副本必须通过从其他副本拷贝来更新至最新状态,可采用版本号机制确定最新副本。由于读操作可在任一副本上执行,所有副本都必须保持最新。
image

每个虚拟分区拥有如下信息,例如在上图中潜在成员为 {V, X, Y, Z},实际成员为 {V, X, Y}。

虚拟分区信息 说明
创建时间 逻辑时间戳
潜在成员集合 所有可能参与的副本管理器
实际成员集合 当前可相互通信的副本管理器共享的视图

新虚拟分区的创建通过一个合作协议实现,由发起者及其可访问的潜在成员共同执行。创建协议的目标是即使在协议执行期间发生真实分区,也能创建一致的虚拟分区。协议分为两个阶段:

  1. 阶段 1:发起者向每个潜在成员发送 Join 请求,附带新虚拟分区的逻辑时间戳。收到请求的副本管理器比较该时间戳与自身当前虚拟分区的时间戳:若请求时间戳更大,则同意加入并回复 Yes;若请求时间戳更小,则拒绝加入并回复 No。
  2. 阶段 2:若发起者收到足够多的 Yes 应答,使其能达到读写法定数,则向同意加入的站点发送 Confirmation 消息,消息中包含新虚拟分区的创建时间戳与实际成员列表。收到确认的副本管理器加入新虚拟分区,并记录其创建时间戳与成员列表。

注意,在阶段 1 回复 Yes 的副本管理器,需在阶段 2 收到 Confirmation 后才正式成为该虚拟分区成员。可能存在多个副本管理器同时尝试创建新分区的情况。例如上图中 Y 和 Z 都在不断尝试连接其他管理器,假设网络部分恢复后 Y 无法与 Z 通信,但两组 {V, X, Y} 和 {V, X, Z} 各自内部可以通信。此时可能形成如下图所示的两个重叠的虚拟分区(V₁ 和 V₂),若两个分区分别执行不同事务,则可能导致读操作在一个分区中的副本 Y 上执行,而写操作在另一分区中执行,两者锁不冲突,从而违背单拷贝串行化。
image
此时可以用最终逻辑时间戳更高的管理器为基准创建分区。该算法在分区不频繁发生的情况下较为高效,在虚拟分区内部的每个事务使用可用拷贝算法执行。

参考资料

《分布式系统概念与设计》[英]George Coulouris, Jean Dollimore, Tim Kindberg, Gordon Blair,金蓓弘, 马应龙 译,机械工业出版社

posted @ 2026-02-12 05:05  乌漆WhiteMoon  阅读(63)  评论(0)    收藏  举报