副本机制与副本同步------《Designing Data-Intensive Applications》读书笔记6
进入到第五章了,来到了分布式系统之中最核心与复杂的内容:副本与一致性。通常分布式系统会通过网络连接的多台机器上保存相同数据的副本,所以在本篇之中,我们来展开看看如何去管理和维护这些副本,以及这个过程之中会遇到的各种问题。
1.副本
在数据系统之中,我们通常会有这样几个原因来使用副本技术:
- 保持地理位置接近用户,从而减少延迟(如:Cache,CDN技术)
- 提高系统的可用性和鲁棒性,即使系统中的某些部分已经失效了,仍然可以对外提供服务。(如:GFS三副本的设计)
- 通过扩展性来提供读查询,从而增加读取吞吐量。(如:ZooKeeper之中的Observer)
首先,如果副本的数据不随时间变化,那么副本的管理是比较简单的:只需要将数据复制到每个节点一次,就OK了。副本管理真正的困难在于对副本数据的修改,这会涉及到很多琐碎的问题。其次,副本复制时要考虑许多权衡,使用同步还是异步复制,以及如何处理失效的副本?接下来我们来一一探讨这个问题。
2.Leader-Follower机制
如何保障多个副本在不同节点上的一致性一直分布式系统之中的一个核心问题。分布式系统在写入数据时,需要由每个副本进行处理;否则,副本将不再包含相同的数据。Leader-Follower是一种常见的机制,我们来梳理一下它的原理:
- 一个节点上的副本被指定为Leader。当客户端需要向系统写入数据时,必须将写入请求发送给Leader,由Leader首先将新数据写入本地存储的副本。
- 管理其他副本的节点称为Follower。每当Leader将新数据写入本地存储d的副本时,也会将数据更改写入日志之中。每个Follower会从Leader那里获取修改日志,并相应地更新数据到的本地副本之中,这样,所有的在Follower上副本的修改顺序会和Leader保持相同的顺序。
- 当客户端需要从系统之中读取数据时,它可以查询Leader或其他Follower。(注:Follower与Leader之中的数据存在延迟,无法保证强一致性)写入请求只能由Leader来响应,或是由Follower转发给Leader。

许多关系数据库在同步副本时使用这样的机制,如PostgreSQL,MySQL,Oracle Data Guard 和SQL Server。同时许多非关系型数据库与分布式消息队列也采用这样的机制,包括MongoDB,Rethinkd,Kafka,RabbitMQ。
2.1 同步与异步复制
在副本进行主从复制时一个重要细节是复制是同步还是异步发生的?(在关系数据库中,这往往是一个可配置的选项。在其他系统之中,如Ceph,是系统默认的)

由上图可知,同步复制有相当大的延迟,而异步复制的响应相当快速。但是异步复制却不能保证完成所需要多长时间。有些情况下,Follower的数据可能比Leader上的数据落后几分钟或更多。如:节点之间存在网络问题或节点的故障恢复。如果Leader失败且不可恢复,则尚未复制到Follower的任何写操作都将丢失。
而同步复制的优点是保证了Follower与Leader之间的副本一致性,一旦任意一个Leader失效了,任何一个Follower的数据都与Leader相同。但是同步复制一旦出现网络或节点的故障,会导致无法处理写入。Leader必须阻止所有写入并等待Follow上的副本再次可用。如果所有的Follower都是同步复制,那么任何一个节点的中断都会导致整个系统瘫痪。在实际运用之中,如果在数据库上启用同步复制,通常其中一个副本是同步复制的,而另一个是异步复制的。如果同步的副本变得不可用或十分缓慢,可以将同步操作切换到另一个异步副本之中。这样保证了至少两个节点上有一个数据的最新副本:Leader和一个同步Follower。这种配置称之为半同步。(链式复制也是类似于半同步的一种复制机制,不丢失数据但仍能提供良好性能和可用性的复制方法。)
2.2 添加新的Follower
有时我们需要添加新的Follower来增加副本的数量,或者替换失败的节点。此时就需要确保新的Follower拥有一个正确的副本的数据。仅仅将数据文件从一个节点复制到另一个节点通常是不够的:客户端不停向系统写入数据,所以数据副本总是处于不断变化的状态。这里可以简单地通过锁定系统,使其拒绝客户端的写请求来使各个副本上保持一致,但这样会大大降低系统的可用性。所以我们需要一个不停机的方式来添加新的Follower:
1.在某个时间点对Leader的副本进行快照,并且将快照复制到新加入的Follower节点。
2 .Follower连接到Leader,并向Leader请求快照之后所有的数据更改。通常是Leader节点的日志序列号。
- 当Follower处理完快照之后的数据更改之后,它就可以正常处理来自Leader的数据更改了。
2.3 节点故障
在分布式系统之中,任何节点都可能出现故障,而能够在不停机的情况下重新启动单个节点是操作和维护是十分必要的。尽管每个节点故障,但我们需要让一个节点停机的影响尽可能小。
- Follower故障
在Follower的本地磁盘上,都保存着从Leader收到的数据更改的日志。当一个Follower崩溃并重新启动,或者Leader与Follower之间的网络暂时中断。Follower可从它的日志找到故障发生之前处理的最后一个事务,然后连接到Leader并请求在Follower断开连接的时候发生的所有数据变化。(这个流程和添加新的Follower其实是同样的思路)
- Leader故障
在处理Leader的失败时显然会更为棘手:其中一个Follower需要被提升为新的Leader,客户端也需要识别并且将后续的请求发送给新的Leader,而其他的Follower则需要开始在新Leader之下工作。处理Leader故障通常是如下的流程:
1、确认Leader失效。绝大多数系统使用超时机制:如果一个节点不响应一段时间,例如,30秒,它被认为是失效了。(如果是中心化的系统可以采用Lease机制。笔者在硕士生阶段对Cassandra数据库有过系统的调研,在Cassandra中采用了由日本学者Naohiro Hayashibara提出的《The Phi Accrual Failure Detector》失败探测算法,通过多维度累积量来判断节点是否失效,不失为一个好的解决方案,十分适合类P2P架构的分布式系统)
2、选取新的Leader。在中心化架构之中,如HDFS,新的Leader可以用中心化节点指定。而在非中心化的架构之中,则可以通过选举过程来完成,分布式系统之中的选举协议有很多:2PC,3PC,Paxos,Raft等等。
3、调整系统配置以使用新的Leader。如果旧的Leader回归到集群,它可能仍然认为自己是Leader,这时需要确保旧的Leader成为Follower并承认新的Leader。
如果是异步复制的场景,新的Leader可能旧的Leader之前的完整的写入信息。最常见的解决方案是丢弃旧Leader之前写入多于新Leader的信息丢弃,但是这显然违反了数据系统写入持久性的要求。
在某些故障场景中,可能会出现两个节点都认为他们是Leader,这种情况被称为脑裂。此时两个Leader都会接受写请求,数据很可能会出现丢失或损坏。
什么时候进行故障切换也是一个值得探讨的问题:较长的超时时间意味着在Leader失效的情况下恢复时间更长。然而,如果时间太短,可能会有不必要的故障转移。例如,临时负载高峰时刻可能导致节点的响应时间增加到超时,那么不必要的故障转移会使情况变得更糟,而不是更好。为此,一些运营团队更愿意执行手动的故障转移,即使系统本身支持自动的故障转移。
3. 日志的复制
日志在副本的一致性之中是至关重要的,所以我们接下来简要的梳理一下日志复制可用的方法:
- Statement-Based复制
在最简单的情况下,Leader将每个写请求通过日志的形式发送给Follower。每个Follower解析和执行对应的操作语句,虽然这听起来很合理,但是实际操作中会存在一些坑:
(1) 非确定性函数,如now()获得当前的日期和时间或rand()得到一个随机数,这样会导致副本之间的不一致。(这里可以转换思维,用一个确定的修改值,来替换不确定性的函数调用)
(2) 如果使用一个自动递增的列,或如果他们依赖于数据库中的现有数据(例如,更新…在<条件>),他们必须执行完全相同的顺序在每个副本,否则也会产生不一致性。(异步转发,乱序到达。这个可以通过操作序列号等强制要求进行规避。)
(3) 有副作用的语句(例如触发器、存储过程、用户定义函数)可能会导致每个副本上出现不同的副作用。
Write-ahead日志复制
日志是一个只包含所有写入操作的字节序列。我们可以使用完全相同的日志来在另一个节点上构建一个副本。Leader将日志写入磁盘之后,将它通过网络发送给Follower。当Follower处理这个日志时,它构建了一个与Leader完全相同的数据结构的副本。这种方式的缺点是:日志在非常低的级别上描述数据。这使得数据拷贝与存储引擎紧密耦合。Row-based日志复制
Row-based与Write-ahead的方法类似,但是它允许复制日志与存储引擎内部分离。这种日志称为逻辑日志,逻辑日志通常是描述在一个行的粒度上记录写入操作:
对于插入的行,日志包含所有列的新值。
对于已删除的行,日志包含足够的信息以唯一地标识删除的行。(主键)
对于更新的行,日志包含足够的信息以唯一地标识更新的行,以及所有列的新值。
由于逻辑日志与存储引擎内部分离,因此可以更容易地保持向后兼容,从而允许Leader与Follower运行不同版本的数据系统,甚至是不同的存储引擎。同时,逻辑日志格式对外部应用程序也更容易解析。可以将逻辑日志的内容发送到外部系统(如用于离线分析的数据仓库),或者用于构建自定义索引和缓存。
4. 复制延迟
副本可以增加系统的可伸缩性(处理比单个机器处理更多的请求)和降低延迟(将副本放置在离用户更近的地方)。写入操作必须通过Leader副本,但是只读查询可以在任何副本上进行。 对于一次写入,多次读取的应用来说,采用读扩展架构是十分合理的。但是由于上文提及的原因,我们通常不会采用同步复制的方式。这将导致数据出现明显的不一致性:如果您同时对Leader和Follwer执行相同的查询,可能会得到不同的结果,因为并不是所有的写入实时在Follower上反馈。这种不一致性仅仅是暂时状态,所以这种情况被称为最终一致性。
对于这种情况我们应该这么去处理和理解,我们下回分解~~~(第五章的内容炒鸡多,接下来会通过多篇读书笔记来给大家梳理,讲解,下一篇再见~~)
副本机制与副本同步------《Designing Data-Intensive Applications》读书笔记6的更多相关文章
- 大数据:Hadoop(HDFS 的设计思路、设计目标、架构、副本机制、副本存放策略)
一.HDFS 的设计思路 1)思路 切分数据,并进行多副本存储: 2)如果文件只以多副本进行存储,而不进行切分,会有什么问题 缺点 不管文件多大,都存储在一个节点上,在进行数据处理的时候很难进行并行处 ...
- python for data analysis 2nd 读书笔记(一)
第一章相对简单,也么有什么需要记录的内容,主要用到的工具的简介及环境配置,粗略的过一下就行了.下面我们开始第二章的学习 CHAPTER 22.2Python Language Basics, IPyt ...
- Kafka 入门(二)--数据日志、副本机制和消费策略
一.Kafka 数据日志 1.主题 Topic Topic 是逻辑概念. 主题类似于分类,也可以理解为一个消息的集合.每一条发送到 Kafka 的消息都会带上一个主题信息,表明属于哪个主题. Kafk ...
- 客户端一致性与多Leader机制------《Designing Data-Intensive Applications》读书笔记7
接着上一篇的内容,我们继续来梳理分布式系统之中的副本机制与副本一致.上文我们聊到了在可用性与一致性之间的一个折中的一致性等级:最终一致性.我们顺着上篇的内容,由用户来分析一致性等级. 1. 客户端的困 ...
- Kafka 存储机制和副本
1.概述 Kafka 快速稳定的发展,得到越来越多开发者和使用者的青睐.它的流行得益于它底层的设计和操作简单,存储系统高效,以及充分利用磁盘顺序读写等特性,和其实时在线的业务场景.对于Kafka来说, ...
- kafka副本机制之数据可靠性
一.概述 为了提升集群的HA,Kafka从0.8版本开始引入了副本(Replica)机制,增加副本机制后,每个副本可以有多个副本,针对每个分区,都会从副本集(Assigned Replica,AR)中 ...
- kafka4 副本机制
概述 每个分区有n个副本,可以承受n-1个节点故障. 每个副本都有自己的leader,其余都是follower. zk中存放分区的leader和 follower replica的信息.(get /b ...
- 深入理解 Kafka 副本机制
一.Kafka集群 二.副本机制 2.1 分区和副本 2.2 ISR机制 2.3 不完全的首领选举 2.4 最少同步副本 ...
- Kafka 学习之路(五)—— 深入理解Kafka副本机制
一.Kafka集群 Kafka使用Zookeeper来维护集群成员(brokers)的信息.每个broker都有一个唯一标识broker.id,用于标识自己在集群中的身份,可以在配置文件server. ...
随机推荐
- mybatis的逆向工程——命令行方式
1.由于MyBatis属于一种半自动的ORM框架,所以主要的工作就是配置Mapping映射文件,但是由于手写映射文件很容易出错,可利用MyBatis生成器自动生成实体类.DAO接口和Mapping映射 ...
- 带你深度解析Maven
一.What`s Maven? Maven是基于项目对象模型(POM project object model),可以通过一小段描述信息(配置)来管理项目的构建,报告和文档的软件项目管理工具,简单的说 ...
- OpenCASCADE 公众号
OpenCASCADE 公众号 eryar@163.com 今天也注册了一个微信公众号,以后会在微信公众号中发表OpenCASCADE学习文章,Blog会与微信公众号同步.下面是微信公众号二维码,欢迎 ...
- iOS 数据加密方案
iOS安全攻防(二十三):Objective-C代码混淆 提交用户的隐私数据 一定要使用POST请求提交用户的隐私数据GET请求的所有参数都直接暴露在URL中请求的URL一般会记录在服务器的访问日志中 ...
- table常用的属性以及用法
<table><table/>先定义一个表格这个就没得讲了,<caption>表示这个表格的标题 <table border="6"> ...
- 数据库入门(以MySQL为例)
一.数据库中的概念 1.数据库是用户存放数据.访问数据.操作数据的存储仓库,用户的各种数据被有组织地存放在数据库中.可以随时被有权限的用户查询.统计.添加.删除.和修改.可以说,数据库是长期存储在计算 ...
- StringUtils工具类常用方法
前言:工作中看到项目组里的大牛写代码大量的用到了StringUtils工具类来做字符串的操作,便学习整理了一下,方便查阅. isEmpty(String str) 是否为空,空格字符为false is ...
- http性能测试工具wrk源码学习之开篇
1.前言 最近工作需要测试nginx反向代理的性能,于是找了一些http测试工具,例如经典的Apache的ab.siege.wrk.wrk使用多线程事件驱动方式,支持lua脚本扩展.关于wrk介绍可以 ...
- Nodejs进阶:crypto模块中你需要掌握的安全基础
本文摘录自<Nodejs学习笔记>,更多章节及更新,请访问 github主页地址. 一. 文章概述 互联网时代,网络上的数据量每天都在以惊人的速度增长.同时,各类网络安全问题层出不穷.在信 ...
- lambda表达式不使用委托(delegate) 用FUNC
lLambda不使用delegate关键字,而使用 Lambda运算符 => goes to l 1.Func<int,string> getInput = (int age ...