9.5 链复制(Chain Replication)

这一部分,我们来讨论另一个论文CRAQ(Chain Replication with Apportioned Queries)。我们选择CRAQ论文有两个原因:第一个是它通过复制实现了容错;第二是它通过以链复制API请求这种有趣的方式,提供了与Raft相比不一样的属性。

CRAQ是对于一个叫链式复制(Chain Replication)的旧方案的改进。Chain Replication实际上用的还挺多的,有许多现实世界的系统使用了它,CRAQ是对它的改进。CRAQ采用的方式与Zookeeper非常相似,它通过将读请求分发到任意副本去执行,来提升读请求的吞吐量,所以副本的数量与读请求性能成正比。CRAQ有意思的地方在于,它在任意副本上执行读请求的前提下,还可以保证线性一致性(Linearizability)。这与Zookeeper不太一样,Zookeeper为了能够从任意副本执行读请求,不得不牺牲数据的实时性,因此也就不是线性一致的。CRAQ却可以从任意副本执行读请求,同时也保留线性一致性,这一点非常有趣。

首先,我想讨论旧的Chain Replication系统。Chain Replication是这样一种方案,你有多个副本,你想确保它们都看到相同顺序的写请求(这样副本的状态才能保持一致),这与Raft的思想是一致的,但是它却采用了与Raft不同的拓扑结构。

首先,在Chain Replication中,有一些服务器按照链排列。第一个服务器称为HEAD,最后一个被称为TAIL。

当客户端想要发送一个写请求,写请求总是发送给HEAD。

HEAD根据写请求更新本地数据,我们假设现在是一个支持PUT/GET的key-value数据库。所有的服务器本地数据都从A开始。

当HEAD收到了写请求,将本地数据更新成了B,之后会再将写请求通过链向下一个服务器传递。

下一个服务器执行完写请求之后,再将写请求向下一个服务器传递,以此类推,所有的服务器都可以看到写请求。

当写请求到达TAIL时,TAIL将回复发送给客户端,表明写请求已经完成了。这是处理写请求的过程。

对于读请求,如果一个客户端想要读数据,它将读请求发往TAIL,

TAIL直接根据自己的当前状态来回复读请求。所以,如果当前状态是B,那么TAIL直接返回B。读请求处理的非常的简单。

这里只是Chain Replication,并不是CRAQ。Chain Replication本身是线性一致的,在没有故障时,从一致性的角度来说,整个系统就像只有TAIL一台服务器一样,TAIL可以看到所有的写请求,也可以看到所有的读请求,它一次只处理一个请求,读请求可以看到最新写入的数据。如果没有出现故障的话,一致性是这么得到保证的,非常的简单。

从一个全局角度来看,除非写请求到达了TAIL,否则一个写请求是不会commit,也不会向客户端回复确认,也不能将数据通过读请求暴露出来。而为了让写请求到达TAIL,它需要经过并被链上的每一个服务器处理。所以我们知道,一旦我们commit一个写请求,一旦向客户端回复确认,一旦将写请求的数据通过读请求暴露出来,那意味着链上的每一个服务器都知道了这个写请求。

最后更新于