DDIA 4 复制

目录

  1. 复制的意义(三点)
  2. 单领导者(同步复制vs异步复制)
  3. 复制延迟问题
  4. 多主复制(适应多数据中心,3种冲突处理方案,3种拓扑结构优缺点)
  5. 无主复制 (2种副本修复办法,Quorum机制,3种写入冲突的解法)

复制的意义

复制意味着在通过网络连接的多台机器上保留相同数据的副本。
他的作用有

  1. 使得数据与用户在地理上接近(从而减少延迟)
  2. 即使系统的一部分出现故障,系统也能继续工作(从而提高可用性)
  3. 扩展可以接受读请求的机器数量(从而提高读取吞吐量)

复制的问题

如果复制中的数据不会随时间而改变,那复制就很简单:将数据复制到每个节点一次就万事大吉。复制的困难之处在于处理复制数据的变更(change)。我们将讨论三种流行的变更复制算法:单领导者(single leader),多领导者(multi leader)和无领导者(leaderless)。几乎所有分布式数据库都使用这三种方法之一。
使用同步复制还是异步复制?如何处理失败的副本?

单领导者(single leader)

image.png

领导者负责写,备份负责读log,同步和领导者的写。log 称为复制日志。

同步复制vs异步复制。


image.png

follower 1 是同步复制。
follower 2 是异步复制。
同步复制的优点是,从库保证有与主库一致的最新数据副本。如果主库突然失效,我们可以确信这些数据仍然能在从库上上找到。
缺点是,如果同步从库没有响应(比如它已经崩溃,或者出现网络故障,或其它任何原因),主库就无法处理写入操作。主库必须阻止所有写入,并等待同步副本再次可用。

异步的优点就是不阻塞主库的写。但是缺点在于从库可能落后主库几分钟或更久;例如:从库正在从故障中恢复,系统在最大容量附近运行,或者如果节点间存在网络问题。

折中策略:
在数据库上启用同步复制,通常意味着其中一个跟随者是同步的,而其他的则是异步的。如果同步从库变得不可用或缓慢,则使一个异步从库同步。这保证你至少在两个节点上拥有最新的数据副本:主库和同步从库。 这种配置有时也被称为半同步(semi-synchronous)

新从库如何同步:
拉取主库的一致性快照。
并拉取快照之后发生的所有数据变更。
当从库处理完快照之后积压的数据变更,代表赶上了。

上述方案也适用于从库挂了。恢复之后怎么做
那么主库挂了该如何?
参见我的文集里的paxos那章。
这些问题没有简单的解决方案。因此,即使软件支持自动故障切换,不少运维团队还是更愿意手动执行故障切换。

复制日志的3种实现

  1. 直接基于语句 (update xxx where xxx)

问题是可能有些函数不是确定的,那么相同语句会跑出不同结果。同时如果基于全局自增的id,则要求这些日志运行的顺序必须严格一致。

  1. 基于WAL

数据库在写数据前一般都会先往wal日志里写一条记录。这个日志只能被该数据库数据引擎自己解析从而构建出一致的数据。所以问题是这使复制与存储引擎紧密耦合。如果数据库将其存储格式从一个版本更改为另一个版本,通常不可能在主库和从库上运行不同版本的数据库软件。

  1. 基于逻辑日志(old val, new val, row id) 又称行日志

为了解决上述问题,可以使复制日志从存储引擎内部分离出来。这种复制日志被称为逻辑日志,以将其与存储引擎的(物理)数据表示区分开来。

  1. 需要自定义的话可以基于触发器来复制,规则自己配。

复制延迟问题

读己之写


image.png

解决思路:只有自己能改的,都从主库读。
可以跟踪上次更新的时间,在上次更新后的一分钟内,从主库读。还可以监控从库的复制延迟,防止任向任何滞后超过一分钟到底从库发出查询。

单调读


image.png

解决方案是可以基于用户ID的散列来选择副本,而不是随机选择副本。

因果读


image.png

确保任何因果相关的写入都写入相同的分区。
对于某些无法高效完成这种操作的应用,还有一些显式跟踪因果依赖关系的算法

多主复制

​ 在单个数据中心内部使用多个主库很少是有意义的,因为好处很少超过复杂性的代价。 但在一些情况下,多活配置是也合理的。


image.png

多主复制在多个数据中心的好处:
1.性能更好
2.可以容忍数据中心停机
3.容忍网络问题

尽管多主复制有这些优势,但也有一个很大的缺点:两个不同的数据中心可能会同时修改相同的数据,写冲突是必须解决的


image.png

解决方案:

  1. 同步与异步冲突检测

  2. 处理冲突的最简单的策略就是避免它们

如果应用程序可以确保特定记录的所有写入都通过同一个领导者,那么冲突就不会发生。由于多领导者复制处理的许多实现冲突相当不好,避免冲突是一个经常推荐的方法

3.收敛到一致状态

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

4.自定义冲突消解的逻辑

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

多Leader机制的复制拓扑

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

多Leader机制的拓扑结构

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

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


写入操作乱序到达

Client A将行插入到一个Leader 1的表,和Client B在Leader 3之中进行更新。而Leader 2收到了不同顺序的写操作:update操作出现在了insert操作之前。为了正确地排列这些事件,我们可以使用一种称为多版本向量控制(MVCC)的技术。 参加我的分布式系统系列文集里 并发控制 一章。

无主复制

多副本读写

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

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

image.png

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

副本修复

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

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

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

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,我们可以容忍两个不可用的节点。

Quorum机制保证了一定能读到最新的副本

写入冲突与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”的关系呢?可以在服务器节点维护一个版本号,每次写操作时递增版本号,并将新版本号存储在写入的值中。

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

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


image.png

image.png

版本向量

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,921评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,635评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,393评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,836评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,833评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,685评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,043评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,694评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,671评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,670评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,779评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,424评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,027评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,984评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,214评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,108评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,517评论 2 343

推荐阅读更多精彩内容

  • 关于Mongodb的全面总结 MongoDB的内部构造《MongoDB The Definitive Guide》...
    中v中阅读 31,894评论 2 89
  • feisky云计算、虚拟化与Linux技术笔记posts - 1014, comments - 298, trac...
    不排版阅读 3,813评论 0 5
  • 缘起 最近研究Spanner,发现国内对Spanner论文的翻译很多,但是美中不足的是,每个人都在做论文的搬运工和...
    吕信阅读 19,813评论 4 36
  • 4. 设计思想 4.1 动机 我们设计的 Kafka 能够作为一个统一的平台来处理大公司可能拥有的所有实时数据馈送...
    疯狂的橙阅读 1,068评论 1 4
  • 分布式系统面临的第一个问题就是数据分布,即将数据均匀地分布到多个存储节点。另外,为了保证可靠性和可用性,需要将数据...
    olostin阅读 4,550评论 2 26