分布式系统primary back up

较弱的一致性

因果一致性

  • 并发的写操作(即,由发生前关系所关联的写操作)必须按照该顺序被看到。并发写操作可以在不同节点上以不同的顺序被看到。
  • 线性一致性意味着因果一致性。

我们可以得到更弱的一致性!

  • FIFO一致性:同一个进程完成的写操作按照该顺序被看到;不同进程的写操作可以以不同的顺序被看到。
  • 最终一致性 ≈ 如果对一个对象的所有写操作停止,最终所有进程读取的值将会相同。

主备份复制

“设计一个实用的容错虚拟机系统”

主备份用例

  • 考虑一个维护状态的高可用服务
    • 文件服务器
    • 银行数据库
  • 在一个节点上保持一致性很容易
    • 只需按照某种明确的顺序执行操作
  • 我们如何同时提供可用性和一致性
    • 文件服务器/数据库服务器可能会崩溃/重启

单节点键/值存储

一般情况:单节点状态机

状态机复制

  • 在多个服务器上复制状态机
  • 客户端可以将所有服务器视为一个状态机
  • 最简单的复制形式是什么?

两个服务器!

  • 在给定时间:
    • 客户端与一个服务器(主服务器)通信
    • 数据在主服务器和备份服务器上被复制
    • 如果主服务器失败,备份服务器将成为主服务器
  • 目标:
    • 正确且可用(就像一个单一的高可用服务器)
    • 尽管有些失败

基本操作

  • 客户端将操作(Put, Get, Append)发送给主服务器
  • 主服务器决定操作的顺序
  • 主服务器将操作序列转发给备份服务器
  • 备份服务器按相同顺序执行操作(热备用)
    • 或者仅保存操作日志(冷备用)
  • 备份服务器执行/保存操作后,主服务器回复客户端

挑战

  • 非确定性操作
  • 主服务器和备份服务器之间的状态转移
    • 操作日志?内存内容?
  • 强一致性
  • 丢弃的消息
  • 服务器故障
  • 一次只能有一个主服务器:客户端、主服务器和备份服务器需要达成一致

视图服务

  • 视图服务器决定谁是主服务器和备份服务器
  • 客户端和服务器依赖视图服务器
  • 视图服务器是单点故障(在实验3中修复)
  • 困难部分:
    • 一次只能有一个主服务器
    • 不在每个请求上都与视图服务器通信

视图

  • 一个视图是关于系统当前角色的声明
  • 视图随时间形成一个序列

检测故障

  • 每个服务器定期向视图服务器发送Ping(Ping RPC)
  • 对视图服务器而言,一个节点
    • 如果错过了n个Ping,则被视为“死亡”
    • 在收到单个Ping后被视为“活着”
  • 服务器是否可能在运行但被声明为死亡?

管理服务器

  • 任何数量的服务器都可以发送Ping
  • 视图服务器维护一组活着的服务器
  • 如果超过两个服务器是活着的,额外的服务器被视为“空闲”
  • 空闲服务器可以被提升为备份

在主服务器故障时

  • 视图服务器声明一个新的“视图”,将备份提升为主服务器
  • 视图服务器提升一个空闲服务器(如果有的话)作为新的备份
  • 主服务器初始化新备份的状态
  • 现在准备处理操作
  • 声明新“视图”的其他场景?
    • 备份失败
    • 视图没有备份,且有空闲服务器

问题

  • 如何确保新的主服务器具有最新状态?
    • 只将备份提升为主服务器
    • 空闲服务器可以在启动时成为主服务器(为什么?)
  • 如果备份还没有获取状态怎么办?
    • 新的主服务器死亡了怎么办?
    • 首要任务:将状态转移给备份

在这个场景中,视图服务通过管理视图(View)来处理主服务器和备份服务器的角色转换。每个视图定义了当前的主服务器和备份服务器。当一个服务器停止发送Ping消息时,视图服务将根据当前的视图和存活的服务器来决定如何转移角色。

视图变化分析
  • 视图1: 主服务器是A,备份服务器是B。当A停止pinging时,需要进行角色转换。
  • 视图2: 由于A停止响应,B被提升为主服务器,C成为新的备份服务器。但是B在提升后立即停止发送Ping消息。
  • 视图3: 由于B也停止响应,C被提升为主服务器。这时,没有可用的备份服务器来填补备份的位置。
如何判断C是否获取了状态

视图服务如何知道C已经获取了状态是一个关键问题。在分布式系统中,确保新的主服务器在接管之前具有最新的状态是非常重要的,以保持数据的一致性和完整性。以下是几种可能的机制:

  1. 状态同步确认机制: 在将备份服务器提升为主服务器之前,原主服务器和备份服务器之间可能会有一个状态同步过程。这个过程可以是同步的,也可以是异步的,但关键是需要有一个明确的确认机制,以便原主服务器(或在其失败后由视图服务)确认备份服务器已成功接收并应用了所有必要的状态信息。这可以通过特定的确认消息或状态同步完成的信号来实现。

  2. 日志复制和确认: 如果系统使用日志复制机制(如Raft或Paxos协议),备份服务器(在本例中为C)可能需要确认它已经接收并应用了到目前为止所有的日志条目。这种确认可以是连续的日志条目应用确认,或者是一个特定的检查点(checkpoint)或快照(snapshot)的应用确认。

  3. 心跳和健康检查: 视图服务可能会依赖于额外的心跳消息或健康检查机制,来验证备份服务器(现在是新的主服务器C)的状态。这可能包括检查C是否处于一个能够正常处理请求的健康状态,以及它是否确认已经获取了最新的系统状态。

  4. 直接通信: 在某些系统设计中,视图服务可能会直接与新的主服务器(C)通信,以确认它是否准备就绪并具有最新的状态。这种通信可能是通过特定的RPC调用或消息交换实现的。

总之,确保新的主服务器具有最新状态的机制取决于系统的具体设计和实现。在高可用性和容错性设计中,这通常涉及到复杂的同步、确认和健康检查机制,以确保在任何时候系统的状态都是一致的,即使在发生故障和角色转换的情况下。

视图服务器等待主服务器的确认

  • 跟踪主服务器是否已确认(通过ping)当前视图
  • 必须保持当前视图直到收到确认
  • 即使主服务器似乎已经失败
  • 这是该协议的另一个弱点

问题

是否可能有多个服务器同时认为自己是主服务器?

分脑问题

1:A,B

A仍然运行,但无法联系到视图服务器 (或者不幸的是ping请求被丢弃)

2:B,_

B得知自己被提升为主服务器 A仍然认为自己是主服务器

在给定的场景中,服务器A因为网络问题或者是不幸的情况(如Ping消息被丢弃)无法到达视图服务器,但它仍然运行并认为自己是主服务器。与此同时,由于A无法与视图服务器通信,视图服务器可能会认为A已经失败,并将B提升为主服务器。这时,B学习到自己被提升为主服务器,而A仍然认为自己是主服务器。这种情况就导致了分脑问题,即系统中有多个主服务器同时存在。

分脑问题导致状态分割?

是否可能有多个服务器同时充当主服务器?

  • 充当=响应客户端请求

当分脑问题发生时,确实有可能导致系统状态分割。因为每个认为自己是主服务器的节点都可能独立响应客户端请求,这会导致数据不一致,因为不同的主服务器可能会有不同的状态更新。

规则

  1. 主服务器的连续性: 视图服务协议规定,视图i+1中的主服务器必须是视图i中的备份服务器或主服务器。这个规则旨在确保状态的连续性和一致性。

  2. 主服务器的响应依赖: 主服务器在回复客户端之前,必须等待备份服务器接受/执行每个操作。这有助于确保在发生故障转移时,数据不会丢失。

  3. 备份服务器的请求接收: 备份服务器只有在视图正确的情况下才接受转发的请求。这有助于防止在分脑情况下处理客户端请求。

  4. 非主服务器的客户端请求处理: 非主服务器必须拒绝客户端请求。这有助于防止在分脑情况下的数据不一致。

  5. 操作与状态转移: 每个操作必须在状态转移之前或之后执行,以确保操作的顺序性和一致性。

规则1:主服务器的连续性

没有规则#1: 分脑问题

  • 场景: 视图从1:A,B变为2:C,D,其中A由于网络问题无法与视图服务器通信,但仍然在运行并认为自己是主服务器。同时,C被提升为新的主服务器,但不知道之前的状态。
  • 分析: 如果没有规则#1,那么新的视图中的主服务器可能与前一个视图完全无关,这会导致新的主服务器缺乏必要的状态信息,从而无法正确处理客户端请求。同时,旧的主服务器A仍然认为自己是主服务器并继续服务,导致系统出现两个源自不同视图的主服务器同时运行,即分脑问题。这将导致数据不一致和状态分割。

规则2:主服务器的响应依赖

没有规则#2: 丢失写入

  • 场景: 客户端向主服务器A写入数据并收到响应,但在A将数据写入备份服务器B之前崩溃。随后,视图变为2:B,C,客户端从B读取数据时,之前的写入丢失。
  • 分析: 规则#2要求主服务器在回复客户端之前必须等待备份服务器接受并执行每个操作。如果没有这条规则,主服务器A崩溃后,B作为新的主服务器可能没有接收到最后的写入操作,导致数据丢失。
“快速”读取?

主服务器是否需要将读取操作转发给备份服务器? (这是一个常见的“优化”手段)

在分布式系统中,为了优化性能和减少延迟,有时会采用“快速”读取操作。这意味着读取操作直接在主服务器上执行,而不是将它们转发到备份服务器。这种方法的目的是减少读取操作的延迟,因为涉及网络通信的操作通常会增加处理请求的时间。

陈旧读取

1: A仍然运行,但无法联系到视图服务器

2: 客户端1向B写入, 客户端2从A读取, A返回过时的值

在分布式系统中,特别是在使用主备份复制模式的系统中,陈旧读取是指客户端从已经过时的服务器(即包含陈旧数据的服务器)读取数据的情况。这种情况通常发生在系统的视图变化之后,某些服务器(在此例中为A)因为网络隔离或其他通信问题未能及时更新其状态以反映最新的视图变更。

发生原因
  • 网络分割:服务器A由于网络问题无法接收到视图服务器的更新消息,因此不知道自己已经不再是主服务器。
  • 视图变更:视图服务器将B提升为新的主服务器,并引入了C作为备份服务器,而A没有收到这一变更。
  • 客户端操作:在这种情况下,如果有客户端向B(现在的主服务器)写入新数据,而另一个客户端尝试从A(过时的服务器)读取数据,就会发生陈旧读取,因为A包含的数据不是最新的。
解决方法

解决陈旧读取问题的关键在于确保所有的读写操作都在最新视图的主服务器上执行,或者至少确保数据的一致性和最新性。这可以通过以下几种方式实现:

  1. 视图一致性:确保所有服务器都及时接收到视图变更通知,并且在视图变更期间暂停服务,直到更新完成。
  2. 读写重定向:如果客户端尝试与非主服务器通信,该服务器应重定向客户端到当前的主服务器。
  3. 心跳和健康检查:通过定期的心跳和健康检查机制,确保所有服务器的状态都是最新的,及时识别并隔离无法正常通信的服务器。

读取与写入
  • 读取操作也被视为状态机操作
  • 顺序一致性如何呢?
  • 写入操作:为了保证数据的一致性和耐久性,通常需要在主服务器和一个或多个备份服务器上执行写入操作。这确保了即使在主服务器失败的情况下,数据也不会丢失,并且系统可以从备份服务器中恢复。
  • 读取操作:在某些情况下,读取操作被视为可以在不牺牲一致性保证的情况下进行优化的操作。通过在主服务器上直接执行读取,可以提高响应速度,特别是在读多写少的应用场景中。

顺序一致性

顺序一致性是一种内存一致性模型,要求操作的结果必须与操作被所有进程执行的顺序一致。在分布式系统中实现顺序一致性意味着,系统中的所有读写操作都必须以一种所有进程都同意的顺序来执行。

  • 如果采用“快速”读取,即在主服务器上直接执行读取操作而不是在所有副本上执行,可能会挑战顺序一致性的实现。因为如果备份服务器上的数据更新延迟,直接从主服务器读取可能会得到最新的数据,而从备份服务器读取可能会得到过时的数据。
  • 要在采用“快速”读取的同时保持顺序一致性,必须确保所有的写入操作在读取之前已经被所有的备份服务器确认。这可能需要额外的同步机制,以确保读取操作只在所有相关的写入操作完成后执行。

规则3:备份服务器的请求接收

没有规则#3: 部分分脑

  • 场景: 在视图1:A,B和视图2:B,C之间,A转发了一个请求。但在请求到达时,视图可能已经变为3:C,D,此时C可能已经是新的主服务器。
  • 分析: 规则#3确保备份服务器只在视图正确的情况下接受转发的请求。没有这条规则,如果A在不知道视图已经变化的情况下转发请求给B(此时C已经是主服务器),会导致系统处于部分分脑状态,因为旧的主服务器A和新的主服务器C可能同时处理请求。

规则4:非主服务器的客户端请求处理

没有规则#4: 不一致

  • 场景: 在视图1:A,B,视图2:B,C,和视图3:B,A的情况下,一个使用过时信息的客户端向A发送请求。如果A响应请求,会违反规则。
  • 分析: 规则#4要求非主服务器必须拒绝客户端请求。如果没有这条规则,当A不再是主服务器时,它响应客户端请求会导致数据不一致,因为此时A可能不具备最新的状态信息或根本不应该处理请求。
主服务器的过时信息

在这个场景中,我们看到视图服务在多次变化后,客户端由于信息过时而向不再是当前主服务器的节点(A)发送请求。这种情况在分布式系统中是可能发生的,尤其是在动态变化的环境中,如服务器故障、网络分区或视图更新延迟等情况下。下面是对这种情况的分析:

发生情况
  • 视图变更序列:系统经历了多次视图变化,从1(A,B)到2(B,C),然后是3(B,A),最后是4(A,D)。
  • 客户端行为:一个信息过时的客户端尝试向A发送请求,可能是因为它没有及时接收到视图更新的通知,仍然认为A是主服务器。
分析
  1. 数据一致性风险:如果A处理了这个过时的请求,可能会导致数据不一致,因为A在当前视图4中不是主服务器,可能没有最新的状态信息。
  2. 系统可靠性问题:这种情况还可能影响系统的可靠性,因为过时的请求可能会基于不一致的数据状态执行,导致错误的决策或操作。
应对策略
  1. 请求重定向:系统可以设计成自动将客户端的请求从非主服务器重定向到当前的主服务器。这要求所有服务器都能识别当前的视图并知道谁是主服务器。
  2. 客户端更新:系统应提供机制,确保客户端能及时接收到视图变更的通知,并更新它们的服务器地址信息,以确保总是向正确的主服务器发送请求。
  3. 请求验证:服务器在处理请求前应验证自己是否为主服务器。如果不是,服务器应拒绝请求或将请求重定向到当前的主服务器。
  4. 版本控制和冲突解决:为数据元素引入版本控制,可以帮助识别和解决由于过时请求导致的潜在冲突。

规则5:操作与状态转移

为什么需要原子状态转移?
  • 在新的备份服务器更新至最新状态之前,存在一个脆弱的窗口期
  • 如果新的主服务器崩溃,会丢失状态
  • 这就是为什么我们需要快速进行状态转移
  • 如果没有并发操作,实现起来更简单
  • 但为什么我们必须原子性地做到这一点?

  1. 状态转移开始:A(新的主服务器)开始向B(新的备份服务器)发送当前状态。
  2. 客户端操作:在A还没将全部状态发送给B的情况下,客户端向A发送了一个操作请求,这个操作修改了系统的状态。
  3. 操作转发:A将这个修改状态的操作转发给B。
  4. B应用操作:B接收并应用了来自A的操作,修改了自己的状态。
  5. 状态转移完成:A继续发送剩余的状态给B,但这部分状态包含了操作前的数据,从而覆盖了B刚刚应用的操作。
  6. A应用操作:A也应用了客户端的操作,更新了自己的状态。
结果
  • 数据不一致:由于A在发送完操作后继续发送了包含操作前数据的状态给B,导致B的状态被覆盖回了操作前的状态。而A应用了操作,两者的状态不再一致。
分析

这个场景揭示了在分布式系统中状态同步过程中的一个关键挑战:如何在状态转移和处理客户端请求之间保持数据的一致性。如果状态转移和客户端操作可以并发发生,就需要确保这些并发操作不会引入数据不一致。

解决策略

为了解决这种类型的一致性问题,可以考虑以下策略:

  • 锁定期间操作:在状态转移期间,暂时锁定对状态的修改,直到状态转移完成。这可以通过暂停客户端请求或者将客户端请求队列化直到状态同步完成来实现。
  • 原子状态更新:确保状态转移是原子的操作,意味着直到新备份完全更新并确认接收了所有状态变更之前,不应用任何新的客户端操作。这可能涉及到更复杂的状态同步协议。
  • 版本控制和冲突解决:为状态引入版本控制,确保状态转移可以识别和整合在转移过程中发生的修改,通过冲突解决机制来保持主备服务器的一致性。

进展

是否存在系统无法继续处理新的客户端请求的情况?

缺乏进展

  • 视图服务器失败
  • 网络完全失败(这个很难解决)
  • 客户端无法联系到主服务器,但可以ping到视图服务器(VS)
  • 没有备份且主服务器失败
  • 主服务器或备份服务器在完成状态转移之前失败

状态转移和RPC

状态转移必须包括RPC数据

重复写入操作

  1. 客户端向A写入:客户端首先向主服务器A发送写入操作。
  2. A转发至B:A处理写入操作后,将该操作转发给备份服务器B以确保数据一致性。
  3. A回复客户端:A完成写入操作并尝试回复客户端,但回复在网络中丢失。
  4. 状态转移至C,B崩溃:在视图变更为2:B,C的过程中,B将当前状态(包括客户端最初的写入操作)转移给新的备份服务器C,并在完成转移后崩溃。
  5. C成为新主服务器:随着视图变更到3:C,D,C成为了新的主服务器。
  6. 客户端重发写入操作:由于客户端没有收到A的回复,它假定写入操作失败,并向当前的主服务器C重发写入请求,导致操作被重复执行。
分析
  • 重复写入的原因:这种重复写入的情况发生的根本原因是客户端未能接收到主服务器A的成功回复,导致它错误地假设操作失败并进行了重试。
  • 数据一致性挑战:这种重复的写入操作可能会对数据一致性造成挑战,特别是如果写入操作涉及到非幂等性操作(即重复执行会改变结果的操作)。
解决策略

为了解决重复写入的问题,可以考虑以下策略:

  • 操作去重:在服务器端实现操作去重机制,如通过唯一的操作ID或时间戳来标识每个客户端请求,使得即使请求被重发,系统也能识别并忽略重复的写入操作。
  • 可靠的消息传递:改进客户端和服务器之间的通信机制,确保操作回复的可靠传递,例如通过使用确认机制或重试策略来处理丢包情况。
  • 幂等性设计:在可能的情况下,设计写入操作为幂等操作,即使操作被多次执行,最终的系统状态仍然是一致的。

多于一个不同视图

视图1下的客户端请求(发送给A)
  • 场景设置:视图服务器停止接收来自A的消息,但A、B以及客户端之间的通信仍然正常。在视图服务器看来,需要将视图从1:A,B变更为2:B,C。
  • 客户端行为:处于视图1的客户端向A发送请求。
  • 预期行为:理论上,A在这一点上可能不再是主服务器,但因为B还没有从视图服务器那里得知视图的变更,A和B都处于不确定状态。A应该处理这个请求,但同时,它也需要与视图服务器同步,确认自己的角色。如果A能与视图服务器通信并得知自己已不是主服务器,理想情况下它应该拒绝请求或将请求重定向到新的主服务器B(尽管B可能还未意识到这一变化)。
视图2下的客户端请求(发送给B)
  • 场景设置:同上,B还没有从视图服务器那里听说过视图变更。
  • 客户端行为:处于视图2的客户端向B发送请求。
  • 预期行为:在最理想的情况下,B作为新的主服务器,应该接受并处理来自客户端的请求。然而,因为B还未意识到自己已经被提升为主服务器,这可能导致处理延迟或请求失败。B需要尽快与视图服务器同步,确认自己的状态,并根据最新的视图处理客户端请求。
分析

这两种情况都凸显了分布式系统中视图同步和角色确认的重要性。在视图变更期间,节点和客户端可能会因为视图更新的延迟而处于不一致的状态,这要求系统设计时需要考虑到这种不确定性,实现机制来处理或缓解这种状态不一致。

解决策略
  • 增强心跳机制:确保所有服务器和视图服务器之间有稳定的心跳机制,以便及时更新和确认当前的视图状态。
  • 请求重定向和回退机制:在服务器接收到客户端请求时,如果服务器发现自己不再是主服务器,应重定向请求到当前的主服务器,或在无法确定当前视图时提供回退处理。
  • 客户端智能:客户端应能处理来自服务器的重定向指示,并有能力重新发现当前的主服务器。

主备份:为什么这很难

  • 主服务器可能失败
  • 备份服务器可能失败
  • 通信可能部分或暂时失败
  • 参与者可能滞后于以下决策:
    • 视图服务器(视图是否已更改?)
    • 主服务器(它是否失败了?是否回复了客户端消息?)
    • 备份服务器(它是否失败了?是否了解到新视图?状态转移是否完成?)
    • 客户端(视图是否已更改?)
  • 9
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值