接着上一篇的内容,我们继续来梳理分布式系统之中的副本机制与副本一致。上文我们聊到了在可用性与一致性之间的一个折中的一致性等级:最终一致性。我们顺着上篇的内容,由用户来分析一致性等级。

1. 客户端的困扰

上篇文章我们提到了数据系统常用的模型,当提交新数据时,必须将它发送给Leader节点,但是当用户查询数据时,可以从一个Follower节点读取该数据。

这样的模型使十分适合Web应用的读多写少的特点。

读写一致性

但是倘若Leader与Follower之间以异步的方式复制的话,会存在一些问题。如下图所示:如果用户数据刚刚写入,而新的数据可能尚未达到Follower节点的副本。在用户的角度,他们提交的数据看起来似乎丢失了。

在这种情况下,我们需要读写一致性。对于用户来说,总是能看到它们最新更新的数据。而其他用户的更新可能需要一定的时间之后才可见。现在新的问题来了,我们如何实现Leader-Follower机制下的读写一致性呢?

这里有一个最简单粗暴的规则是:用户可以选择总是从Leader节点那里读取自己写入的数据,然后选择自从Follower节点处读取其他用户写入的数据。(注:这里的技巧十分巧妙,十分适合在多用户下的隔离,但是仅仅适用于每个用户都仅仅修改自己数据的场景。)

所以更好的方式是时间戳机制,客户端可以通过记录最近一次写入的时间戳,然后数据系统需要确保为该用户提供的任何读取的副本至少在该时间戳之后更新。如果一个副本还没有达到最新的时间戳,则该读取需要由另一个副本处理,或者等待可本节点的副本跟进到满足要求的时间戳。时间戳可以是逻辑时间戳(表示写入顺序的命令,如日志序列号)或实际系统时钟(强依赖系统时钟的话,需要处理时钟回拨等问题,十分麻烦~~~)。

单调读一致性

解决了读写一致性,我们再来看看下面的这个场景:

因为用户可以从多个不同的副本进行多次读取,则可能发生这种情况。如上图所示,用户2345进行了两次相同的查询,第一次访问了Follower1节点,第二次查询访问了Follower2节点。第一次查询返回一个最近由用户1234添加的注释,但是第二次查询并没有上次查询的注释了,因为滞后的Follower还没有同步到之前的写入注释的操作。如果用户2345第一次看到用户1234的注释出现,然后再次查询它时却消失了,这对用户2345来说是非常令人困惑的。

单调读一致性来是保证这种异常不会发送。当客户读到的数据,保证不会看到一个旧的数据。要满足单调读一致性。实现单调读取的一种方法是确保每个用户总是从同一副本中读取(不同的用户可以从不同的副本读取)。例如,可以根据用户ID散列选择副本,而不是随机选择。

多数据中心下的交叉设备读

在多个数据中心的环境下,问题会变的更加复杂。任何需要由Leader节点服务的请求都必须路由转发到包含Leader的数据中心。当同一用户从多个设备访问服务时,另一个复杂的问题出现了,例如桌面Web浏览器和移动应用程序。在这种情况下,您可能希望在读写一致性的基础之上提供交叉设备读:如果用户输入某个设备上的一些信息,然后在另一个设备上查看,则应该看到他们刚刚输入的信息。如果需要提供交叉设备读,记录用户上次更新的时间戳是十分困难的,因为一个设备不知道其他设备上发生了什么更新。假如你的副本分布在不同的数据中心,也不能保证不同设备的连接将被路由引导到同一数据中心。

小结:当使用一个最终一致性的数据系统时,如果复制延迟增加到几分钟甚至几小时,就需要考虑应用程序的行为。如果答案是“没有问题”,那太好了。但如果应用程序对一致性敏感,则应用程序需要提供额外的处理逻辑来处理特殊的场景,如对某些特殊的读取操作,可以限定只对Leader节点执行某些类型的读取。但是,在应用程序代码中处理这些问题会很复杂,很容易出错。事务确保了许多一致性模型,使应用程序更简单。然而,在向分布式的环境之中,许多数据系统放弃了对事务的支持,因为事务会大大降低分布式环境之中系统的性能与可用性。所以,最终一致性能够使用的场景有限,我们还是要按需选择,避免踩坑。

2. 多Leader机制

在多数据中心的环境下,如果仅仅只有一个Leader,所以每次写操作都必须访问同一个数据中心,这将会导致延迟大大提高。所以我们可以考虑多Leader的机制。在多Leader机制可以在每个数据中心中设置一个Leader。下图展示了多Leader机制的结构:



在数据中心内部,保持前文提到的Leader-Follower机制。而跨数据中心的Leader之间通过冲突协调进行数据同步。我们来梳理一下多Leader机制的一些特点

  • 性能

在多Leader机制中,每个写操作可以在本地数据中心进行处理,再异步复制到其他的数据中心。因此可以大大降低跨数据中心的网络延迟,性能表现显然会更好。

  • Leader失效

在单Leader的机制里,如果数据中心失效,则故障转移可以使另一个数据中心中的Follower成为Leader。而多Leader机制,每个数据中心可以独立于其他数据中心继续运行。

  • 网络的延迟与故障

数据中心之间的通信通常依托于公共互联网,它相比数据中心内的本地网络更加不可靠。显然具有异步复制特性的多Leader机制可以更好地容忍跨数据中心通信的延迟与故障。

写冲突

虽然多Leader机制具备了很多优势,它也有一个大缺点是:相同的数据可以在两个不同的数据中心,一旦数据同时被修改就必须要有机制来解决写冲突的问题。如下图所示,考虑一个同时由两个用户编辑的wiki页面。User1将页面标题从A改为B,User2同时将标题从A改为C。每个用户的更改都分别成功提交给了Leader1与Leader2。当进行异步复制时,系统会检测到冲突:

在一个单Leader的数据系统之中,User2要么阻塞,等待第一次写入完成,要么中止第二个写事务,迫使User2重试写入。而在多Leader机制之中,两个写入操作都是成功的,并且冲突只是在稍后的某个时间点异步检测到的。有什么办法可以解决这样的问题呢?

  • 避免冲突

    避免冲突:如果应用程序可以确保某个特定记录的所有写入都由同一个Leader处理,那么冲突就不会发生。由于多Leader机制处理冲突十分复杂,避免冲突是经常推荐的方法。(在用户可以编辑自己的数据的应用程序中,可以确保特定用户的请求总是路由到同一个数据中心,并使用该数据中心中的Leader处理读写请求。不过这只是一种鸵鸟策略,用户地理位置的转移,或者是路由系统的更新,冲突协调仍然不可避免。

  • 收敛到一致状态

    在单Leader的机制中:如果对同一个字段有多个更新,最后一个写入确定字段的最终值。。而在多Leader的机制中,没有定义的写入顺序,因此不清楚最终值应该是什么。所以数据系统必须以收敛的方式解决冲突,这意味着当所有更改都被复制时,所有副本必须到达相同的最终值。可以为每个写操作分配一个唯一的ID(例如,一个时间戳,一个长的随机数,一个UUID或散列的键和值),最高的ID值认为是最终值,这种技术被称为Last Write Win(LWW)。(强依赖系统时间又会造成很多问题,唉,这真的很烦

  • 自定义冲突消解的逻辑

    最合适的解决冲突的方法可能取决于应用程序,该代码可以在写或读时执行:一旦数据系统检测到复制更改日志中的冲突,它就调用冲突处理程序。或是在应用程序读取的阶段检测到冲突时,会将这些数据的多个版本将返回应用程序。应用程序可以提示用户或自动解决冲突,并将结果写入数据库。(Cassandra与CouchDB就是采取了这种机制

多Leader机制的复制拓扑

两个Leader进行同步时,拓扑结构十分简单。但是一旦扩展到4,5个Leader,之后多个Leader之间的同步结构又应该是怎么样的呢?(虽然在实践中,很少采用这样的架构

最一般的拓扑结构是图(c),其中每个节点都将其写入传递给所有的节点。而(a)或(b)采用了环形或星型的结构来减少网络的流量。在环形和星形拓扑中,在到达所有副本之前,写入可能需要经过几个节点。因此,节点需要转发它们从其他节点接收到的数据更改。为了防止无限复制循环,每个节点都被赋予唯一的标识符,并且在复制日志中,每个写入都用它经过的所有节点的标识符标记。当一个节点接收一个带有自己标识符的数据更改时,该数据更改将被忽略,因为节点知道它已经被处理了。

环形和星形结构存在的一个问题是,如果有一个节点失效,会中断其他节点之间的同步消息流,而因为它不允许消息沿着不同的路径传播,造成了单点故障。但是All pass的结构也会带来一些新的问题,由于网络拥塞的原因,各个节点的信息接收顺序不一致,如下图所示:

Client A将行插入到一个Leader 1的表,和Client B在Leader 3之中进行更新。而Leader 2收到了不同顺序的写操作:update操作出现在了insert操作之前。为了正确地排列这些事件,我们可以使用一种称为多版本向量控制(MVCC)的技术。至于什么是MVVC,我们下一篇继续来梳理~~( 不是我故意卖关子啊,只是怕写的太长你们懒得看~~~

客户端一致性与多Leader机制------《Designing Data-Intensive Applications》读书笔记7的更多相关文章

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

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

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

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

  3. P2P结构与Quorum机制------《Designing Data-Intensive Applications》读书笔记8

    前文涉及到了很多与Leader相关的算法,大家有木有想过,王侯将相,宁有种乎,既然Leader这么麻烦,干脆还是采用P2P模型吧,来个大家平等的架构.本篇需要和大家探讨的就是多副本下实现民主政治的Qu ...

  4. 线性一致性与全序广播------《Designing Data-Intensive Applications》读书笔记12

    上一篇聊了聊构建分布式系统所面临的困难,这篇将着重讨论构建容错分布式系统的算法与协议.构建容错系统的最佳方法是使用通用抽象,允许应用程序忽略分布式系统中的一些问题.本篇我们先聊一聊线性一致性,以及与线 ...

  5. 分布式系统的一致性算法------《Designing Data-Intensive Applications》读书笔记13

    一致性算法是分布式系统中最重要的问题之一.表面上看,这似乎很简单,只是让几个节点在某些方面达成一致.在本篇之中,会带大家完整的梳理分布式系统之中的共识算法,来更加深刻的理解分布式系统的设计. 1.原子 ...

  6. 《android开发艺术探索》读书笔记(二)--IPC机制

    接上篇<android开发艺术探索>读书笔记(一) No1: 在android中使用多进程只有一种方法,那就是给四大组件在AndroidMenifest中指定android:process ...

  7. 《Java并发编程的艺术》读书笔记:二、Java并发机制的底层实现原理

    二.Java并发机制底层实现原理 这里是我的<Java并发编程的艺术>读书笔记的第二篇,对前文有兴趣的朋友可以去这里看第一篇:一.并发编程的目的与挑战 有兴趣讨论的朋友可以给我留言! 1. ...

  8. 可靠的、可扩展的、可维护的数据系统 ------《Designing Data-Intensive Applications》读书笔记1

    坦白说也是机缘巧合,在硕士生阶段进入分布式系统领域学习.无论是大规模存储或计算,其核心也是运用分布式技术利用并行性来解决数据密集型应用的需求.最近开始在啃这本<Designing Data-In ...

  9. 《android开发艺术探索》读书笔记(十)--Android的消息机制

    接上篇<android开发艺术探索>读书笔记(九)--四大组件 No1: 消息队列MessageQueue的内部存储结构并不是真正的队列,而是采用单链表的数据结构来存储消息列表,因为单链表 ...

随机推荐

  1. C++ 头文件系列(set)

    简介 头文件包含set.multiset两个类模版,这里要描述的概念与map非常相似,甚至连成员函数都几乎一样,所以这篇随笔会很短. set set如果翻译成中文应该是集合的意思,这里更确切的说是唯一 ...

  2. magento获取商品的图片

    获取商品的图片主要从catalog_product_entity_media_gallery 表中 该表中各列的属性代表 value_id:记录 ID,可以留空让数据库自动生成. attribute_ ...

  3. Xamarin Android Gestures详解

    通过Gesture的监听我们将实现一个,手指的快速滑动显示坐标的变化,我们先来看一看效果图: 1.Android中手势交互的执行顺序 1.手指触碰屏幕时,触发MotionEvent事件! 2.该事件被 ...

  4. Q:javax.comm 2.0 windows下Eclipse的配置

    @转自http://blog.csdn.net/zhuanghe_xing/article/details/7523744处 要在Windows下,对计算机的串口或并口等进行编程,可以选择使用Java ...

  5. PHP常用功能模块

    错误异常模块 错误处理 1. 系统定义了一些二进制码,用来表示错误报告的级别:     在 /etc/php5/apache2/php.ini中修改php配置文件,其中display_errors默认 ...

  6. Python学习_11_类和实例

    类和实例 类是对象创建实例的模板,而实例则是对象的实体.类使用class关键字定义: class MyClass:    pass python中创建实例直接使用工厂函数(类名加上一对括号),和其他的 ...

  7. Micropython教程之TPYBoardv102 DIY蓝牙智能小车实例

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

  8. Java 读取配置文件

    1.读取XML文件使用dom4j-full.jar包的SAXReader解析: Document document=new SAXReader.reader("xml文路径/文件名xxx.x ...

  9. 【Java框架型项目从入门到装逼】第六节 - 用ajax请求后台数据

    这一节我们来说一下如何用ajax提交请求? 我们先不讲ajax的原理,还是先以实战为主,看一下这个东西到底怎么用的? form表单: <!-- 采用post表单提交 --> <for ...

  10. css3实现图片旋转效果

    css3实现图片旋转效果 近期实现一个消息提醒(醒目)的需求页面.想到了css3的旋转动画,故使用. =============== 鼠标悬浮时候,图片可以旋转,放大 rotate(360deg) s ...