Files
MIT6.824/lecture-06-raft1/6.1-split-brain.md
2022-01-25 02:41:31 +00:00

7.8 KiB
Raw Permalink Blame History

6.1 脑裂Split Brain

在之前的课程中我们介绍了几个具备容错特性fault-tolerant的系统。如果你有留心的话你会发现它们有一个共同的特点。

  • MapReduce复制了计算但是复制这个动作或者说整个MapReduce被一个单主节点控制。
  • GFS以主备primary-backup的方式复制数据。它会实际的复制文件内容。但是它也依赖一个单主节点来确定每一份数据的主拷贝的位置。
  • VMware FT它在一个Primary虚机和一个Backup虚机之间复制计算相关的指令。但是当其中一个虚机出现故障时为了能够正确的恢复。需要一个Test-and-Set服务来确认Primary虚机和Backup虚机只有一个能接管计算任务。

这三个例子中它们都是一个多副本系统replication system但是在背后它们存在一个共性它们需要一个单节点来决定在多个副本中谁是主Primary

使用一个单节点的好处是它不可能否认自己。因为只有一个节点它的决策就是整体的决策。但是使用单节点的缺点是它本身又是一个单点故障Single Point of Failure

所以你可以认为我们前面介绍的这些系统它们将系统容错的关键点转移到了这个单点上。这个单点会在系统出现局部故障时选择数据的主拷贝来继续工作。使用单点的原因是我们需要避免脑裂Split-Brain。当出现故障时我们之所以要极其小心的决定数据的主拷贝是因为如果不这么做的话我们可能需要面临脑裂的场景。

为了让同学们更深入的了解脑裂我接下来会说明脑裂带来的问题以及为什么这是个严重的问题。现在假设我们将VMware FT中的Test-and-Set服务构建成多副本的。之前这是一个单点服务而VMware FT依赖这个Test-and-Set服务来确定Primary虚机所以为了提高系统的容错性我们来构建一个多副本的Test-and-Set服务。我们来看一下为什么出现故障时很难避免脑裂。

现在我们来假设我们有一个网络这个网络里面有两个服务器S1S2这两个服务器都是我们Test-and-Set服务的拷贝。这个网络里面还有两个客户端C1C2它们需要通过Test-and-Set服务确定主节点是谁。在这个例子中这两个客户端本身就是VMware FT中的Primary和Backup虚拟机。

如果这是一个Test-and-Set服务那么你知道这两个服务器中的数据记录将从0开始。任意一个客户端发送Test-and-Set指令这个指令会将服务器中的状态设置成1。所以在这个图里面两个服务器都应该设置成1然后将旧的值0返回给客户端。本质上来说这是一种简化了的锁服务。

当一个客户端可以与其中一个服务器通信但是不能与另一个通信时有可能出现脑裂的问题。我们假设客户端发送请求时它会将请求同时发送给两个服务器。这样我们就需要考虑当某个服务器不响应时客户端该怎么做或者说某个服务器不响应时整个系统该如何响应更具体点我们假设C1可以访问S1但是不能访问S2系统该如何响应

一种情况是我们必然不想让C1只与S1通信。因为如果我们只将C1的请求设置给S1而不设置给S2会导致S2的数据不一致。所以我们或许应该规定对于任何操作客户端必须总是与两个服务器交互而不是只与其中一个服务器交互。但是这是一个错误的想法为什么呢因为这里根本就没有容错。这里甚至比只使用一个服务器更糟。因为当两个服务器中的一个故障了或者失联了我们的系统就不能工作了。对于一个单点的服务我们只依赖一个服务器。现在我们有两个服务器并且两个服务器都必须一致在线这里的难度比单个服务器更大。如果这种方式不是容错的我们需要一种行之有效的方法。

另一个明显的答案是如果客户端不能同时与两个服务器交互那它就与它能连通的那个服务器交互同时认为另一个服务器已经关机了。为什么这也是一个错误的答案呢因为我们的故障场景是另一个服务器其实还开机着。我们假设我们经历的实际问题并不是这个服务器关机了因为如果关机了对我们来说其实更好。实际情况可能更糟糕实际可能是网络线路出现了故障从而导致C1可以与S1交互但是不能与S2交互。同时C2可以与S2交互但是不能与S1交互。现在我们规定如果一个客户端连接了两个服务器为了达到一定的容错性客户端只与其中一个服务器交互也应该可以正常工作。但是这样就不可避免的出现了这种情况假设这根线缆中断了将网络分为两个部分。

C1发送Test-and-Set请求给S1S1将自己的状态设置为1并返回之前的状态0给C1。

这就意味着C1会认为自己持有锁。如果这是一个VMware FTC1对应的虚拟机会认为自己可以成为主节点。

但是同时S2里面的状态仍然是0。所以如果现在C2也发送了一个Test-and-Set请求本来应该发送给两个服务器但是现在从C2看来S1不能访问根据之前定义的规则那就发送给S2吧。同样的C2也会认为自己持有了锁。如果这个Test-and-Set服务被VMware FT使用那么这两个VMware 虚机都会认为自己成为了主虚拟机而不需要与另一个虚拟机协商,所以这是一个错误的场景。

所以,在这种有两个拷贝副本的配置中,看起来我们只有两种选择:要么等待两个服务器响应,那么这个时候就没有容错能力;要么只等待一个服务器响应,那么就会进入错误的场景,而这种错误的场景,通常被称为脑裂。

这基本是上世纪80年代之前要面临的挑战。但是当时又的确有多副本系统的要求。例如控制电话交换机的计算机系统或者是运行银行系统的计算机系统。当时的人们在构建多副本系统时需要排除脑裂的可能。这里有两种技术

  • 第一种是构建一个不可能出现故障的网络。实际上不可能出现故障的网络一直在我们的身边。你们电脑中连接了CPU和内存的线路就是不可能出现故障的网络。所以带着合理的假设和大量的资金同时小心的控制物理环境比如不要将一根网线拖在地上让谁都可能踩上去。如果网络不会出现故障这样就排除了脑裂的可能。这里做了一些假设但是如果有足够的资金人们可以足够接近这个假设。当网络不出现故障时那就意味着如果客户端不能与一个服务器交互那么这个服务器肯定是关机了。
  • 另一种就是人工解决问题,不要引入任何自动完成的操作。默认情况下,客户端总是要等待两个服务器响应,如果只有一个服务器响应,永远不要执行任何操作。相应的,给运维人员打电话,让运维人员去机房检查两个服务器。要么将一台服务器直接关机,要么确认一下其中一台服务器真的关机了,而另一个台还在工作。所以本质上,这里把人作为了一个决策器。而如果把人看成一台电脑的话,那么这个人他也是个单点。

所以,很长一段时间内,人们都使用以上两种方式中的一种来构建多副本系统。这虽然不太完美,因为人工响应不能很及时,而不出现故障的网络又很贵,但是这些方法至少是可行的。