前文涉及到了很多与Leader相关的算法,大家有木有想过,王侯将相,宁有种乎,既然Leader这么麻烦,干脆还是采用P2P模型吧,来个大家平等的架构。本篇需要和大家探讨的就是多副本下实现民主政治的Quorum机制。至于它是怎么样解决我们在前文提及的各种问题的,接着这篇文章我们继续聊聊~~

1. No-Leader机制

有些数据存储系统放弃了Leader的机制,允许任何副本直接接受用户的写操作。(如Amazon的Dynamo,FaceBook的Cassandra,虽然最终FaceBook放弃了Cassandra转而支持Hbase,但是Uber的强势介入让Cassandra后来在开源社区大放异彩。) 每个接受到客户端写请求的节点会转换为一个协调器节点,而协调器节点不强制执行特定的写入顺序。正是这种设计上的差异对数据库的使用方式与数据模型产生了深远的影响。

多副本的读写

No-Leader机制是怎么样消除Leader这个角色的存在的呢?答案也很简单:多副本读写。接下来我们来看一个栗子:

假设我们在数据系统之中采用了三副本的结构,如下图所示:User 1234 并行地将所有的副本发送给三个存储节点,并且两个节点可以接受副本的写入,但是其中一个节点不在线,所以副本写入失败。所以在三个副本中有两个副本确认写入成功了:在User 1234收到两个OK响应之后,User就认为写入操作是成功的,忽略了一个副本写入失败。(当然,不是简单的就不管了,后续会有修复机制来补齐这个副本的数据

现在假设User 2345开始读取新写入的数据。由于一个节点写入失败了,所以User 234 可能会得到过期的值作为响应。为了解决这个问题,当User 从数据系统之中读取数据时,它不只是将请求发送到一个副本,而是将读取请求并行地发送到多个副本节点。User可以从不同节点获得不同的响应,即来自其他节点的最新值和另一个节点的过期值。这里通过了版本号用于确定哪个值是更新的值。

副本修复

No-Leader机制导致了数据系统之中可能存在大量过期的值,所以一个节点怎么来修复自身的副本来获取最新值的过程我们就称之为副本修复,No-Leader机制也是通过这样的方式来达到最终一致性的。通常会有这样几种方式:

  • 读修复

    当用户并行读取多个节点时,它可以获取到其他过期的值的响应。所以用户会发现其中有些节点拥有过期的值,这时用户可以主动将新值写入该节点。这种方法称之为读修复

  • 反熵过程(其实是一个物理学概念)

    每个数据存储节点都会有一个后台进程,不断的比对自己的副本与其他节点副本的差异,发现自己拥有过期的值之后,会主动修复自己过期的副本。与基于写入顺序日志不同,这种反熵过程不以任何特定的顺序复制写操作,并且在复制数据之前可能会有显著的延迟。

2. Quorum机制

上文之中提及的例子在三个副本中的两个之上写入成功,我们认为写操作成功了。但是如果三个副本只有的一个副本写入成功了?这时的写操作是否是成功的呢?

答案是否定的?这里其实就是简单的鸽巢原理,这里我不做数学证明了,大家有兴趣的可以自行证明一下。

假设有n个副本,每次写操作必须由w个节点确认为成功,每个读操作读取r个节点。(在上文的例子中,n=3,w=2,r=2)。只要w + r > n,如果读和写操作的总次数大于n,那么读和写操作必然至少有一个副本是相同的,也就是读操作必然可以读到最新写操作的数据。这被我们称之为:Quorum机制,每次读写都需要达到法定人数。

通常 n、w和r通常是可配置的,根据您的需要来修改这些数字。一个常见的选择是使n为奇数(通常为3或5),并设置w=r=(n + 1)/ 2 。如下图所示,如果w < n,如果有n - w个节点不可用,我们仍然可以处理写操作。同样的如果r<n,如果有n - r个节点不可用,我们仍然可以处理读操作。而如果小于所需的w或r节点可用,则写或读操作就会返回错误。

  • n=3,w=2,r=2,我们可以容忍一个不可用的节点。
  • n=5,w=3,r=3,我们可以容忍两个不可用的节点。

高可用与Hinted handoff

Quorum机制实现了最终一致性的模型,但是在可用性上还是有一些极端情况,没法很好处理。如:出现网络抖动时,但是可能系统仍然有许多正常工作的节点。但是副本应该被写入的n个节点发生网络问题,导致了会少于w或r个成功的读写操作,由于不能达到法定的人数,读写操作都会失败。所以这时候数据库系统的设计者面临权衡取舍,能不能通过一些机制,实现更好的可用性呢?

所以这种情况下,我们就可以利用Hinted handoff了(原谅我翻译不好)。这种方式是怎么样实现的呢?写和读操作仍然需要w和r成功的响应,但是可以不强制一定要写如指定的n个节点 (这个涉及到一致性哈希,数据分布的知识,暂时要是理解不了,我后续会有专门的专题来写这个内容,可以先放一放。) 打个比方说,如果你把自己锁在门外,你可能会敲邻居的门,问你是否可以暂时呆在他们的沙发上,一旦你找到钥匙了,你就自己回家了。所以其他节点可以暂存本应该放在另一个节点上的副本,一旦网络中断被修复,其他节点就会把副本转交给主人节点。

所以这种模式既保证了不违反Quorum机制,也大大提高了系统的可用性,被No-leader数据系统广泛采用。

3 写入冲突与Quorum机制

同样的Quorum机制的设计本身就可以允许并发读写操作,并容忍网络中断与高峰延迟。但是这也必然会带来一致性问题,我们来看下面这个例子:

如图所示,有两个Client A与B,同时写入关键字X在一个三副本的数据存储系统之中。Node 1接收来自A的写入,但由于网络中断而从未接收来自B的写入。Node 2首先接收来自A的写入,然后接收B写入。而Node 3则是首先接收来自B的写入,然后接收A的写入。Node 2认为X的最终值是B,而其他Node认为最终值是A.

在这样的场景下如何仲裁写入结果成为了一个大问题,思路和我们之前提到的类型:

  • Last Write Win

    我们可以为每个写操作附加一个时间戳,选择最大的时间戳作为最新的值,并丢弃任何具有早期时间戳的写操作的值。这种冲突解决算法,称为Last Write Win。这种情况要求每个写操作具有幂等性,否则会出现写丢失的情况,如何能保证不出现依赖的写丢失呢?

  • 合并“happens-before”关系

    每当有两个操作A和B时,有三种可能:A发生在B之前,B发生在A之前,A或B是并发的。我们需要的是一个算法,告诉我们两个操作是否并发。如果一个操作在另一个操作之前发生,那么后面的操作应该覆盖前面的操作,但是如果操作是并行的,那么我们需要解决一个冲突。怎么样去捕获并合并“happen-before”的关系呢?可以在服务器节点维护一个版本号,每次写操作时递增版本号,并将新版本号存储在写入的值中。

    • 客户端

      当客户端读取一个键时,服务节点会返回所有未被覆盖的值,以及最新版本号。当客户端需要写一个键时,它必须包含从先前读取中的版本号,并且它必须合并它在前面读取中接收到的所有值。
    • 服务器

      当服务器接收到具有特定版本号的写入时,它可以覆盖该版本号或以下的所有值,因为它知道已经合并到新值,但必须保留所有值具有更高版本号。
  • 版本向量

合并“happen-before"使用一个单一的版本号来捕捉操作之间的依赖关系,但这不足以解决当有多个副本并行写入的情况。相反,我们需要使用每个副本的版本号以及每个键。每个副本在处理写时递增自己的版本号,并跟踪从其他副本中看到的版本号。此信息指示要覆盖哪些值以及作为兄弟版本保存着哪些值。而所有副本的版本号的集合称为版本向量。版本向量从数据节点发送给客户端,所以版本向量让我们可以区分覆盖写与发并行写操作。

4. 小结

好了,到此为止我们终于总结完整了分布式系统之中的副本机制。从Leader-Follower 机制到多Leader机制,最后到No-Leader的机制,并且详细总结了各个机制的实现细节与优缺点,希望大家阅读完之后也能有所收获。

P2P结构与Quorum机制------《Designing Data-Intensive Applications》读书笔记8的更多相关文章

  1. python for data analysis 2nd 读书笔记(一)

    第一章相对简单,也么有什么需要记录的内容,主要用到的工具的简介及环境配置,粗略的过一下就行了.下面我们开始第二章的学习 CHAPTER 22.2Python Language Basics, IPyt ...

  2. 分布式入门之2:Quorum机制

    1.  全写读1(write all, read one) 全写读1是最直观的副本控制规则.写时,只有全部副本写成功,才算是写成功.这样,读取时只需要从其中一个副本上读数据,就能保证正确性. 这种规则 ...

  3. 副本机制与副本同步------《Designing Data-Intensive Applications》读书笔记6

    进入到第五章了,来到了分布式系统之中最核心与复杂的内容:副本与一致性.通常分布式系统会通过网络连接的多台机器上保存相同数据的副本,所以在本篇之中,我们来展开看看如何去管理和维护这些副本,以及这个过程之 ...

  4. 分布式系统理论之Quorum机制

    一,Quorum机制介绍 在分布式系统中有个CAP理论,对于P(分区容忍性)而言,是实际存在 从而无法避免的.因为,分布系统中的处理不是在本机,而是网络中的许多机器相互通信,故网络分区.网络通信故障问 ...

  5. JVM内存结构,运行机制

    三月十号,白天出去有事情出去了一天,晚上刚到食堂就接到阿里电话, 紧张到不行,很多基础的问题都不知道从哪里说了orz: 其中关于JVM内存结构,运行机制,自己笔记里面有总结的,可当天还是一下子说不出来 ...

  6. 写一致性原理以及quorum机制

    (1)consistency,one(primary shard),all(all shard),quorum(default)我们在发送任何一个增删改操作的时候,比如 PUT /index/type ...

  7. Quorum机制与NRW算法总结

    Quorum机制与NRW算法总结 1.Quorum机制 Quorum,原指为了处理事务.拥有做出决定的权力而必须出席的众议员或参议员的数量(一般指半数以上). 2.NRW算法 NRW算法是基于Quor ...

  8. 分布式系统读写模型中的Quorum机制

    分布式系统的设计中会涉及到许多的协议.机制用来解决可靠性问题.数据一致性问题等,Quorum 机制就是其中的一种.我们通过分布式系统中的读写模型来简单介绍它. 分布式系统中的读写模型 分布式系统是由多 ...

  9. java集合专题 (ArrayList、HashSet等集合底层结构及扩容机制、HashMap源码)

    一.数组与集合比较 数组: 1)长度开始时必须指定,而且一旦指定,不能更改 2)保存的必须为同一类型的元素 3)使用数组进行增加/删除元素-比较麻烦 集合: 1)可以动态保存任意多个对象,使用比较方便 ...

随机推荐

  1. 环形进度条的实现方法总结和动态时钟绘制(CSS3、SVG、Canvas)

    缘由: 在某一个游戏公司的笔试中,最后一道大题是,“用CSS3实现根据动态显示时间和环形进度[效果如下图所示],且每个圆环的颜色不一样,不需要考虑IE6~8的兼容性”.当时第一想法是用SVG,因为SV ...

  2. popupwindow那些坑

    1. new PopupWindow(vw, ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT); 如果 ...

  3. 鸟哥的linux私房菜学习-(四)linux命令的基本概念

    一.命令格式及使用方式 注意到上面的说明当中,『第一个被输入的数据绝对是命令或者是可运行的文件』! 这个是很重要的概念喔!还有,按下[Enter]键表示要开始运行此一命令的意思. 如下效果一样: 二. ...

  4. 鸟哥的linux私房菜学习-(三)X Window与文本模式的切换

    通常我们也称文本模式为终端机接口, terminal 或 console喔!Linux默认的情况下会提供六个Terminal来让使用者登陆, 切换的方式为使用:[Ctrl] + [Alt] + [F1 ...

  5. Head First设计模式之策略模式

    这是学习的第一个设计模式,而书中写的实例相对比较复杂,参考了网上的文章进行总结 一.定义 策略模式(strategy pattern): 定义了算法族, 分别封闭起来, 让它们之间可以互相替换, 此模 ...

  6. Web服务器(Apache)与Servlet容器(Tomcat)

    之前一直比较迷惑Apache与Tomcat的关系,通过查询资料,有所了解,现记录于此. Apache与Tomcat 两者定位:Apache是HTTP Web服务器,Tomcat是Web容器. 有一个非 ...

  7. springMVC(7)---表单标签

    springMVC(7)---表单标签 form标签作用                                     简单来讲form表单有两大作用 1:第一就是往后端提交数据或者前端回显 ...

  8. Micropython教程之TPYBoard制作蓝牙+红外循迹小车

    1.实验目的 学习在PC机系统中扩展简单I/O接口的方法. 进一步学习编制数据输出程序的设计方法. 学习蓝牙模块的接线方法及其工作原理. 学习L298N电机驱动板模块的接线方法. 学习蓝牙控制小车的工 ...

  9. AspNet Core Web 应用程序的启动 当项目中 没有Startup.cs 类如何设置启动 配置等等

    感叹: Core 16年6月1号 在中国宣布上线 到现在已经快经历两年时间了,目前版本已经到了2.0 就目前的前景来看,个人感觉 到2020年才可能有所起色,等到Core更成熟 个人看法:在.net这 ...

  10. 【Python3之内置函数】

    内置函数 简单来说就是python3本身就自带的函数. abs(x) abs()返回一个数字的绝对值.如果给出复数,返回值就是该复数的模 print(abs(-1100)) 输出:1100 all() ...