深入浅出 Raft - 基本概念

作者: siddontang | 来源:发表于2017-10-14 11:54 被阅读4800次

引子

因为一直在跟 Raft 打交道,虽然对 Raft 很熟悉了,但如果你要我去给一个完全不知道什么是 Raft 的人讲 Raft,我觉得难度还是非常大的。所以我决定使用我一贯罗里吧嗦,用比喻和讲故事的方式,来尝试说说 Raft。

如果你跟你孩子一起看过小猪佩奇,你大概就能知道我为啥用了这么怪的取名。如果没看过的,强烈推荐你去看看,这真的是一部很不错的儿童动画。

日志和状态机

兔小姐准备在泥坑小镇成立一家银行(就叫泥坑银行吧)。对于银行储蓄系统的设计,兔小姐找来了猪爸爸。

兔小姐:『猪爸爸,我们要保证,无论怎样用户的金钱不能有错误。假如客户存了 100 块钱,那么他的账户就会多出来 100 块钱,不会是 101,也不会是 99。』
猪爸爸:『好的,兔小姐,我觉得我们可以这样。如果一个客户来存钱,那么,首先我们可以将交易依次顺序记录下来,然后兔先生再把客户实际的钱放到金库对应的保险柜里面。当兔先生把钱放好之后,我们就可以告诉客户这笔交易完成了』
兔小姐:『好主意,猪爸爸,不过为什么要先将交易记录下来呢?直接放到金库不就可以了吗?』
猪爸爸:『首先,如果交易记录都没成功,那么我们就不用再麻烦将钱放到金库了。其次,假设同时很多人来存钱,兔先生有点处理不过来,没准就会弄错。我们有记录的话,兔先生就可以按照记录一个一个处理,虽然这样慢一点,但不会出错。另外,交易记录永远不可能被篡改或者被覆盖,譬如如果我们在记录 N 这个位置记录下客户 A 存了 100 块钱,那么这条记录 N 后面一定是这笔交易,而不可能在变成客户 B 取了 100 块钱。』
兔小姐:『嗯,听起来很有道理,那么我们就这么做吧。』

好了,虽然上面的例子有点不切合实际,毕竟如果银行真这么玩,离倒闭也就不远了,但各位还是先认为这套机制能很好的工作吧。在 Raft 里面,交易记录,我们可以叫做日志,而金库,则是状态机。对于任何的操作,Raft 会首先将其记录到日志,然后等这个日志提交之后,我们再将其对应的数据写入到状态机里面。每一条日志都有一个唯一的编号,这个编号是严格按照加一单调递增的,也就是说,leader 只会追加日志,而不会覆盖日志。假设现在的日志编号是 10,那么下一条日志的编号就一定是 11。如果这条日志被应用到了状态机,那么我们就可以认为这条日志已经是被 applied 了。

Quorum

不久之后,泥坑银行储蓄系统第一个版本就上线了。一切工作的良好,直到有一天,电闪雷鸣,泥坑银行停电了。用户发现根本没办法进行交易了,虽然狗爷爷尽了最大的努力,但让整个银行正常工作也花了不少时间。银行正常营业之后,兔小姐找来了猪爸爸。

兔小姐:『猪爸爸,现在看起来我们必须要保证,即使在泥坑小镇的银行出现了问题,譬如停电这种的,用户仍然能够正常的进行交易。』
猪爸爸:『是的,兔小姐。那要做到这样,我们必须在其他地方建立另一个银行网点,这样,即使在泥坑小镇银行不能对外提供服务了,但客户还是能在其他银行网点进行交易。』
兔小姐:『好主意,猪爸爸,那么我们 就在回音山谷建立一个分部,当泥坑小镇的银行出现了故障,用户仍然能够在回音山谷进行交易。』
猪爸爸:『兔小姐,恐怕只是在回音山谷建立一个分部,是不行的。』
兔小姐:『为什么呢?猪爸爸,我有点不明白了。』
猪爸爸:『现在假设我们在泥坑小镇和回音山谷都部署好系统,如果一个客户到泥坑小镇存钱,我们首先要在泥坑小镇这边记录这笔交易,然后在通知回音山谷那边也记录这笔交易,只有我们知道回音山谷记录交易成功了,我们才可以进行下一步,也就是将用户的钱放到金库里面,同时告诉回音山谷那边,也需要将对应钱放到那边的金库,这样如果泥坑小镇出现了问题,客户仍然能到回音山谷那边取钱。』
兔小姐:『这听起来很复杂,但这样看起来没问题,所以将系统放到两个地方,没问题呀!』
猪爸爸:『不不,兔小姐。上面我说的是都两边都能正常工作的情况,但实际会有很多异常情况。譬如,假设泥坑小镇这边的系统能正常工作,但回音山谷的出现了问题,那么客户来泥坑小镇存钱,因为我们没办法在回音山谷记录这笔交易,所以用户仍然不能存钱。』
兔小姐:『出现这种情况,我们还是先让客户能存钱吧,等回音山谷系统好了再把相关的交易记录放到那边去,这样不行吗?』
猪爸爸:『当然不可以,兔小姐。因为我们要保证客户金钱的绝对安全。假设客户先在泥坑小镇这边存了钱,回音山谷那边可能因为出现了问题并不知道这笔交易,如果泥坑小镇这边的系统出现了问题,那么用户去回音山谷取钱,就会发现,他在回音山谷的钱还是之前的总额,这样问题就大了。所以,如果只有两个地方有系统,我们必须要保证这两个地方的系统都完全能正常工作,任何一方出现了问题,整个系统就是不可用的。』
兔小姐:『哦,我大概明白了,那我们怎么办?』
猪爸爸:『如果我们要容忍一个地方的银行不能对外提供服务,但客户还是能正常的进行交易,我们至少需要在三个地方部署系统。』
兔小姐:『哦,我有点糊涂了,你能仔细解释下吗,为什么三个就可以呢?』
猪爸爸:『好的,兔小姐。假设现在我们在三个地方有部署好了系统,譬如这三个地方就是泥坑小镇,回音山谷和海盗岛吧。假设一个客户来泥坑小镇存钱,首先,我们会在泥坑小镇记录下这笔交易,然后告诉回音山谷和海盗岛也记录下这次交易,如果回音山谷或者海盗岛有一个回复泥坑小镇这边交易已经被成功记录,我们就可以允许客户在泥坑小镇将钱存到金库了,然后在告诉回音山谷和海盗岛那边可以存入金库了。』

猪爸爸停顿了一下,喝了一口水,接着道:『上面我们说到,只要我们知道有两个地方成功记录下这次交易,我们就可以继续存钱了,即使一个地方出现了问题也不会有问题。譬如,我们知道泥坑小镇和回音山谷成功记录了交易,但海盗岛因为一些问题导致了反馈延迟,但还能正常工作。然后泥坑小镇这边突然出现了问题,不能对外提供服务了,但我们还是能正常对外提供服务,因为我们知道最新的交易信息已经被记录到了回音山谷,我们从回音山谷这边就一定能得到正确的金钱总数。但是,这时候我们仍然只有两个地方能正常工作了,所以如果第二个地方出现了问题,我们仍然不能对外提供服务了。所以,如果我们要容忍两个地方出现问题,但系统仍然能够对外提供服务,我们就需要——』

『我们就需要在五个地方部署服务了,是吧,猪爸爸。』兔小姐直接插话道。
『是的,非常正确,兔小姐。』猪爸爸由衷的赞叹道。
『那么我觉得我们先考虑三个地方吧,容忍一个地方不能工作就可以了,那就在回音山谷和海盗岛那边也建立分部吧。』
『好的,兔小姐,不过其实我有点担心海盗岛那边。。。。。。』
『就这么决定了,猪爸爸』,兔小姐没等猪爸爸说完,就直接做了决定。

好了,说了这么多,还是回到现实中来吧。上面的例子,我们可是假设了金钱能被复制成多份放到不同的金库里面的,但现实银行可不会这么干。为了要设计一个高可用的系统,单点问题是必须要解决的,毕竟如果这个点出现了问题,整个系统就没法服务了。为了解决这个问题,我们需要在多个地方部署系统,但这样就会引入另一个问题,也就是数据一致性的问题。

CAP

这里我们来简单说说 CAP,也就是一致性,可用性和分区容忍性。因为在分布式系统里面,P 一定是避免不了的,所以我们无非就是选择 C 或者选择 A 的问题。通常 A 都是能做到 HA,也就是高可用的,所以对于需要完全保证数据安全的系统,我们一定会选择 C。为了保证 C,我们在写入数据的时候,一定会保证至少 quorum 的节点都成功被写入了数据,才会认为这次写入是成功的。 在 Raft 里面,如果一条日志被 quorum 的节点成功接收,那么我们就可以认为这条日志已经被 committed 了。

通常,我们说的 C,其实就是线性一致性,也就是我在某个时间写入了一个值,那么这个时间点之后的任何时间,我们读到的就是这个最新的值,而不可能是老的。在数据写入 quorum 节点之后,我们的读取如果也能够保证在 quorum 节点读取,那么就一定能读到最新的值。这个就是 Amazon 的 Dynamo 做法,但这样就把线性一致性保证的负担落到了读取数据的客户端上面。Raft 采用了另一种简单的做法,我们后续继续说明。

小结

好吧,说了这么多,说了这么多,其实也就提了几个 Raft 的概念。这里稍微总结一下,Raft 使用的是 Log Replication + State Machine 的方式来处理分布式数据的一致性问题,这也是现在的通用做法。对于 Raft 来说,Log 的 ID 一定是加一单调递增的,如果一个 Log 被至少 quorum 个节点接受,我们就可以认为这条日志被 committed 了,然后就可以应用这条 Log,当 Log 被应用之后,改 Log 就是 applied 了。后面,我们将开始讨论 Raft 的 Leader 了。

相关文章

  • 深入浅出 Raft - 基本概念

    引子 因为一直在跟 Raft 打交道,虽然对 Raft 很熟悉了,但如果你要我去给一个完全不知道什么是 Raft ...

  • RocketMQ主从切换

    本文主要是记录raft协议的学习过程,包括如下几个方面 raft协议一些基本概念 raft协议场景 raft协议在...

  • 转载:深入浅出etcd之raft实现

    转载:深入浅出etcd之raft实现 导语 etcd是coreOS使用golang开发的分布式,一致性的kv存储系...

  • 深入浅出 Raft - Optimization

    在猪爸爸的努力下,三个银行网点能正确的选出一个主网点对外提供服务了。一切工作的良好,但随着客户的增多,一些问题渐渐...

  • 深入浅出 Raft - Leader 选举

    Leader 很快,泥坑银行就在回音山谷和海盗岛建立了网点。这时候,兔小姐就对猪爸爸说到:『猪爸爸,现在我们已经有...

  • 深入浅出 Raft - Membership Change

    在猪爸爸的努力下,泥坑银行终于能高效正常的运作了,但猪爸爸一直比较担心海盗岛那边的网点,因为他总是担心跨海的通讯会...

  • 深入剖析共识性算法 Raft

    一、 Raft简介 1.1 Raft简介 Raft[https://ramcloud.atlassian.net/...

  • Raft算法可视化

    Raft可视化raft模型:http://thesecretlivesofdata.com/raft/多个节点的一...

  • raft算法笔记

    raft算法动画地址:http://thesecretlivesofdata.com/raft/raft是一个共识...

  • fabric1.4.1新特性 — raft排序服务

    1. RAFT排序服务介绍 在fabric1.4.1的版本中,提供了基于raft共识的raft排序服务。raft的...

网友评论

  • 新牛哥:按你的逻辑,容忍二个地方不能工作,只需要部署在4个地方就可以了啊,因为剩余的2个都正常记录了数据啊
    _飞翔的梵高:假设容忍2个错误,部署4个地方,因为你容忍2个错误,所以至少需要确认3个地方已经记录了一笔交易,我们就假设A B C D四个地方吧,A B C都记录了这笔交易,D也许落后一些,没关系。然后A B发生错误挂掉了,此时没有关系,因为C还保持正确的记录,系统还是可以工作的。过了一会A B都重新起来了,注意这个时候C D需要把记录同步到A和B,但是此时如果C和D都发生错误挂掉了(系统还是容忍两个错误,因为A B已经起来了),A和B拿不到正确的记录,所以整个系统是不能工作的。
    所以这个时候我们需要E,并且C D E都必须保持正确的记录,这样即使他们其中的两个挂掉了,我们也可以确保正确的记录不会丢掉,所以容忍两个错误需要5个地方,并且最少有3个地方确认一笔交易就OK。
    一个咸鱼程序员:总共四个,两个不可用,剩下两个,两个节点可能会出现数据不一致性,无法判断哪份数据是正确的,如果剩下3个,其中两个数据写成功了,且数据一致,就可以认为当前系统整体数据是写成功了。

本文标题:深入浅出 Raft - 基本概念

本文链接:https://www.haomeiwen.com/subject/pluxuxtx.html