Tagged: leader

0

无leader replication的实现和问题介绍

我们在前面的《多leader replication的实现及常见问题介绍》和《分布式系统之leader-followers Replication深入介绍》分别介绍了多leader和单leader的情况,也许你会好奇是否有无leader的实现呢?答案是肯定的,本文就来深入介绍无leader replication的实现和相关的问题。 其实最早期的时候有很多无leader的实现,就是任何节点都可以进行写。后来慢慢大家就不太使用这个实现了,直到Amazon推出了它的Dynamo系统,这一实现又再次流行起来。现在Riak,cassandra以及Voldermort都是开源的无leader的数据库实现。 如何处理有节点出问题的写 在有leader的架构中,当有节点出现问题(假如在leader上),我们需要做failover,然后才能继续写。而在一个没有leader的结构中,failover就不存在,所以当有节点出问题时,整个流程如下图所示:客户端会给每一个节点发送写的请求,我们现在假设三个节点中有一个失败了,并且我们认为大多数节点写成功就是一个成功写,这里有两个节点写成功了,所以整个写操作是成功的,因此对user1234来说,这个写是ok的。 这样的实现问题很明显,比如说现在节点3恢复了,这个时候,任何到replica3的读都会有问题,因为它丢失了在出问题那段时间的写操作。一个比较简单的解决方案就是,读操作也不是只发送给一个节点,我们同时给三个节点发送,然后根据返回数值所在的version来决定谁是最新的,谁最新我们就使用谁。 读修复和反熵(Anti-entropy) 在分布式系统中,我们希望每一个replica最终都是一样的,所以就希望上文提到的replica3在回来之后能够通过一定的手段修复,就是得到它offline这段时间发生的写操作。那么如何才能达到这个效果呢?一般有以下的方法: 读修复(read pair) 当客户端从多个replica中读取数据的时候,它能看到哪个是最新的,哪些是还有问题的。所以这个时候一个比较常见的方法就是把它看到的最新的值写到它看到的有问题的节点中。这种方法在读比较多的时候很有效,其实可以想象,假如没有读,就没有 机会去比较和重写。 反熵 这个就是后台会有一个线程来不停地检查各个replica之间的差别,当发现有数据差别的时候,就会进行修复,这个和leader不同的是,这个修复不是顺序的,而且可能delay比较大。 Quorums读和写 我们在上面的例子中提到三个节点,有两个写成功,并且我们的读也是两个的话,就可以保证读到最新的写。其实这种读写我们称之为Quorums读和写。准确来讲,假如我们有n个节点,然后写的节点是w个,读的节点是r个,那么quorums读写就是保证w+r>n即可,因为这就意味着我们的读写节点必然有一个是交叉的。也就意味着肯定能读到最新的内容。 就如上图所示,n=5,w=3,r=3,这样哪怕有两个节点是有问题的,我们仍然可以看到replica3包含了最新的写的内容,同时它也被读操作读到了。 虽然上面的定义中是要求w+r>n,其实在现实中,我们很多时候的实现还是会选择>n/2,因为这就基本意味着两者的和肯定大于n了。 但是,是否w+r>n我们就一定什么问题也没有了呢?其实并不尽然,下面是一些常见的可能出问题的情况: 假如写和读同时发生,而这个写只写到了某一些节点,就很难说读到的是不是最新的值了。 假如写是部分成功的,比如说w=3,但是只有两个节点成功了,这个写是失败的,但是并没有去roll back成功的两个写,那么这时读的值很难确定是不是最新的了。 假如一个有新的值的节点突然出现问题了,然后我们从有旧的值得节点来进行replica,这个时候存有新的值的节点就被覆盖成旧的值了,这也就破坏了我们提到的quorum的要求了。 所以,尽管这种方法看起来不错,但是还是有可能会出现这样或那样的问题。 Sloppy Quorum和Hinted Handoff 我们在上文中提到的w+r>n 其实是一种严格的quorum要求,那么假如现实中我们在写的时候w设置的3,但是只有2个节点写成功了,是接受这个写的结果还是直接返回错误呢?你当然可以选择直接返回错误,但是其实还有一种实现就是在这种情况下,我们在n个节点之外再找一个节点去写一下,也保证写了w个节点,只是这里可能有不在n个节点中的节点出现,我们称这种情况为Sloppy Quorum。 当出问题的节点回来之后,任何写到第三节点的值我们需要再写回这个节点,这个过程我们称之为hinted handoff。...

3

分布式系统之leader-followers Replication深入介绍

我们在前面有简单讲过Replication的作用,简单说就是为在多个机器上保存同样的拷贝来服务的。有了这个拷贝之后我们就可以做很多事情,比如说它可以成为一个读的源从而分散读的压力,它可以在原来数据机器出问题(或者deploy)等的时候作为一个backup等等。 这个想法其实很简单,但真正在我们做这个拷贝的时候,会遇到很多问题,比如说我们是使用同步还是使用异步来进行同步多个拷贝,如何保证多个拷贝之间的一致性等等。那么本文就来从各个方面详细介绍这些内容。 Leaders和Followers 我们把每一个保存数据的节点称之为replica,当我们有多个节点的时候,最明显的一个问题就是怎么去保证每个节点的内容都是一样的呢?其中最常见的方法就是基于leader的模式(也成为master-slave模式或者active/passive模式)。总得来说,它的工作方法如下: 一个节点是leader。所有的写操作都必须经过leader。 其他的节点我们成为follower,每次leader写数据的时候,也把相关的内容发送到每一个follower(replication log),然后每个follower根据这些log来更新本地的数据。 当有读的操作的时候,既可以从leader读也可以从follower读。 同步VS.异步Replication 上面这种方法其实在很多关系型数据库中很常见。这里遇到的一个最大的问题就是使用同步还是异步来进行replication。 就比如上面的这个case,它实现的功能很简单。就是把user id 1234这个用户的picture更新一下,它首先发送了update的请求到了leader的replica上。在这之后,leader会把这个更新的请求分别发送给两个follower的replica,从而最终达到所有的replica上这个用户的picture都更新了的效果。 我们就以这个例子来分析一下同步和异步在这两个上面的差别。如下图所示,我们看到leader的返回是在Follower1 更新完成之后,也就是说follower1是一个同步的更新。而leader并没有等待follower2完成更新再返回,这也就意味着follower2的更新是一个异步的。 从这个图中可以看到follower1和follower2的更新其实都是由一个延时的,尽管大多数时候这个延时都很小,但是当我们遇到网络问题或者别的情况的时候(CPU占用率很高,内存不够等等),这个延时有可能会很大,谁也不能保证什么时候能被更新。 所以,同步更新的好处就是follower其实和leader是同步的,当leader有问题的时候我们甚至可以直接切换到同步更新的follower,当然它的问题也很明显,就是每一个更新(写操作)都需要等待这个follower的更新完成才行,这有可能会导致整个request延迟很久,甚至超时。所以,假如你想做同步的更新,一般来说也不会想让所有的节点都同步,而是选择leader和一个节点是同步的,这样就可以在leader出问题的切换和同步的效率之间达到一个很好的trade off。 当然,事实情况下基于leader的replication基本都是完全使用异步。这样的问题就是leader出问题,那些没有sync到别的node的数据就会丢失。好处就是leader完全不收别的节点的影响,哪怕别的节点都出问题也可以正常独立运行(通常这个时候就会出alert,然后oncall就到了表现的时候了,哈哈)。生产环境中这样的选择其实有很多原因,比如说为了保证随时都有节点是健康的,这些节点通常会分布在不同的地区(数据中心),所以他们之间的通信通常不是那么可靠,如果使用同步就会很容易出现问题等等。 Follower的建立 有时我们需要从无到有建立一个新的follower,这种情况在生产环境中特别常见,比如说机器的磁盘坏了,原有follower的数据就都不能用了,这个时候就需要从头开始建立,或者说某个时刻你没有足够的follower了(比如说数据中心出问题了,或者机器出问题了),你需要在一个新的机器上建立一个 follower,那么如何从头开始来建立一个节点呢? 首先想到的就是从leader上拷贝过来就好了,但是我们知道leader其实是在不停改变的,也就是说随时都在不停地被写入,而数据的拷贝速度不会很快(主要瓶颈在HDD的写入速度上,目前基本在200MB/s左右),所以在这个过程中不允许写可能也不是一个很好的方法。那么一般来说是怎么做的呢?大概会有以下的步骤: 得到一个数据库某一个节点的snapshot(这个snapshot不影响写)。大多数数据库都支持这样的功能。 拷贝这个snapshot到新的follower Follower联系leader去得到所有的在这个snapshot之后的操作(log sequence)。 然后follower根据得到的信息来apply snapshot之后 的操作,我们称这个过程为catch up。 处理节点的中断 我们上文中也提到了任何节点在任何时候都有可能出问题。那么要是真的出问题了,我们一般会怎么处理呢? 假如这个出问题的节点是follower,假如这个问题只是service...