DDIA 读书分享 第五章:Replication,多主模型

简介: DDIA 读书分享会,会逐章进行分享,结合我在工业界分布式存储和数据库的一些经验,补充一些细节。每两周左右分享一次,欢迎加入,网站在这里: https://ddia.qtmuniao.com/ 。我们有个对应的分布式&数据库讨论群,每次分享前会在群里通知。如想加入,可以加我的微信号:qtmuniao,简单自我介绍下,并注明:分布式系统群。


单主模型一个最大问题:所有写入都要经过它,如果由于任何原因,客户端无法连接到主副本,就无法向数据库写入。

于是自然产生一种想法:多主行不行?

多主复制(multi-leader replication):有多个可以接受写入的主副本,每个主副本在接收到写入之后,都要转给所有其他副本。即一个系统,有多个写入点

多主模型应用场景

单个数据中心,多主模型意义不大:复杂度超过了收益。总体而言,由于一致性等问题,多主模型应用场景较少,但有一些场景,很适合多主:

  1. 数据库横跨多个数据中心
  2. 需要离线工作的客户端
  3. 协同编辑

多个数据中心

假设一个数据库的副本,横跨多个数据中心,如果使用单主模型,在写入时的延迟会很大。那么每个数据中心能不能各配一个主副本?

image.png

                         multi-leader across multiple data centers

单主和多主,在多数据中心场景下的对比:

对比项 单主模型 多主模型
性能 所有写入都要路由到一个数据中心 写入可以就近
可用性 主副本所在数据中心故障,需要有个切主的过程 每个数据中心可以独立工作
网络 跨数据中心,写入对网络抖动更敏感 数据中心间异步复制,对公网容错性更高

但是多主模型在一致性方面有很大缺陷:如果两个数据中心同时修改同样的数据,必须合理解决写冲突。另外,对于数据库来说,多主很难保证一些自增主键、触发器和完整性约束的一致性。因此在工程实践中,多主用的相对较少。

离线工作的客户端

离线工作的一个应用的多个设备上的客户端,如果也允许继续写入数据。如:日历应用。在电脑上和手机上离线时如果也支持添加日程。则在各个设备联网时,需要互相同步数据。

则离线后还继续工作的多个副本,本质上就是一个多主模型:每个主都可以独立的写入数据,然后在网络连通后解决冲突。

但,如何支持离线后正常地工作,联网后优雅的解决冲突,是一个难题。

Apache CouchDB 的一个特点便是支持多主模型。

协同编辑

Google Docs 等类似 SaaS 模式的在线协同应用越来越流行。

这种应用允许多人在线同时编辑文档或者电子表格,其背后的原理,与上一节离线工作的客户端很像。

为了实现协同,并解决冲突,可以:

  1. 悲观方式。加锁以避免冲突,但粒度需要尽可能小,否则无法允许多人同时编辑一个文档。
  2. 乐观方式。允许每个用户无脑写入,然后如果有冲突,交由用户解决。

git 也是一个类似的协议。

处理写入冲突

多主模型最大的问题是:如何解决冲突。

image.png

write conflict

考虑 wiki 一个页面标题的修改:

  1. 用户 1 将该页面标题从 A 修改到 B
  2. 用户 2 将该页面标题从 A 修改到 C

两个操作在本地都修改成功,然后异步同步时,会出现冲突。

冲突检测

有同步或者异步的方式进行冲突检测。

对于单主模型,当检测到冲突时,由于只有一个主副本,可以同步的检测冲突,从而解决冲突:

  1. 让第二个写入阻塞,直到第一个写完成。
  2. 让第二个写入失败,进行重试。

但对于多主模型,两个写入可能会在不同主副本立即成功。然后异步同步时,发现冲突,但为时已晚(没有办法简单决定如何解决冲突)。

虽然,可以在多主间使用同步方式写入所有副本后,再返回请求给客户端。但这会失掉多主模型的主要优点:允许多个主副本独立接受写入。此时,蜕化成单主模型。

冲突避免

解决冲突最好的方式是在设计上避免冲突

由于多主模型在冲突发生后再去解决会有很大的复杂度,因此常使用冲突避免的设计。

假设你的数据集可以分成多个分区,让不同分区的主副本放在不同数据中心中,那么从任何一个分区的角度来看,变成了单主模型。

举个栗子:对于服务全球用户的应用,每个用户就近固定路由到附近的数据中心。则,每个用户信息都有唯一的主副本。

但如果:

  1. 用户从一个地点迁移到了另一个地点
  2. 某个数据中心损坏,导致路由变化

就会对该设计提出一些挑战。

冲突收敛

在单主模型中,所有事件比较容易进行定序,因此我们总可以用后一个写入覆盖前一个写入。

但在多主模型中,很多冲突无法定序:从每个主副本来看,事件顺序是不一致的,并且没有哪个更权威一些,那么就无法让所有副本最终收敛(convergent)

此时,我们就需要一些规则,来让其收敛:

  1. 给每个写入一个序号,并且后者胜。本质上是使用外部系统对所有事件进行定序。但可能会产生数据丢失。举个例子,对于一个账户,原有 10 元,客户端 A - 8,客户端 B - 3,任何一个单独成功都有问题。
  2. 给每个副本一个序号,序号更高的副本有更高的优先级。这也会造成低序号副本的数据丢失。
  3. 提供一种自动的合并冲突的方式。如,假设结果是字符串,则可以将其排序后,使用连接符进行链接,如在之前 Wiki 的冲突中,合并后的标题为 “B/C”
  4. 使用程序定制一种保留所有冲突值信息的冲突解决策略。也可以将这个定制权,交给用户。

自定义解决

由于只有用户知道数据本身的信息,因此较好的方式是,将如何解决冲突交给用户。即,允许用户编写回调代码,提供冲突解决逻辑。该回调可以在:

  1. 写时执行。在写入时发现冲突,调用回调代码,解决冲突后写入。这些代码通常在后台执行,并且不能阻塞,因此不能在调用时同步的通知用户。但打个日志之类的还是可以的。
  2. 读时执行。在写入冲突时,所有冲突都会被保留(如使用多版本)。下次读取时,系统会将所有数据本版本返回给用户,进行交互式的或者自动的解决冲突,并将结果写回系统。

上述冲突解决只限于单个记录、行、文档层面。

TODO(自动冲突解决)

界定冲突

有些冲突显而易见:并发写同一个 Key。

有些冲突则更隐晦,考虑一个会议室预订系统。预定同一个会议室不一定会发生冲突,只有预定时间段有交叠,才会有冲突。

多主复制拓扑

复制拓扑(replication topology)描述了数据写入从一个节点到另一个节点的传播路径。

在只有两个主副本时,拓扑是确定的,如图 5-7。Leader1 和 Leader 都得把数据发给对方。但随着副本数的增多,数据复制拓扑就会有多种选择,如下图:

image.png

                        multi-leader topologies

上图表示了 ≥ 4 个主副本时,常见的复制拓扑:

  1. 环形拓扑。通信跳数少,但是在转发时需要带上拓扑中前驱节点信息。如果一个节点故障,则可能中断复制链路。
  2. 星型拓扑。中心节点负责接受并转发数据。如果中心节点故障,则会使得整个拓扑瘫痪。
  3. 全连接拓扑。每个主库都要把数据发给剩余主库。通信链路冗余度较高,能较好的容错。

对于环形拓扑和星型拓扑,为了防止广播风暴,需要对每个节点打上一个唯一标志(ID),在收到他人发来的自己的数据时,及时丢弃并终止传播。

全连接拓扑也有自己问题:尤其是所有复制链路速度不一致时。考虑下面一个例子:

image.png

                                        writes wrong order

两个有因果依赖的(先插入,后更新)的语句,在复制到 Leader 2 时,由于速度不同,导致其接收到的数据违反了因果一致性。

要想对这些写入事件进行全局排序,仅用每个 Leader 的物理时钟是不够的,因为物理时钟:

  1. 可能不能够充分同步
  2. 同步时可能会发生回退

可以用一种叫做版本向量(version vectors)的策略,对多个副本的事件进行排序,解决因果一致性问题。下一节会详细讨论。

最后忠告:如果你要使用基于多主模型的系统,一定要知晓上面提到的问题,多做测试,确保其提供的保证符合你的使用场景。


我是青藤木鸟,一个喜欢摄影的分布式系统程序员。欢迎关注我的公众号:木鸟杂记。如果觉得不错,就点个在看分享一下吧。如果想找人交流分布式系统和数据库,欢迎来论坛:https://distsys.cn/ 提问,点击下方阅读原文可直达。

参考资料

[1]DDIA 读书分享会: https://docs.qq.com/sheet/DWHFzdk5lUWx4UWJq

相关文章
|
12月前
|
消息中间件 缓存 Java
五个月,秒杀,38个大的篇章,126+篇文章、视频、小册,150+源码分支,完美收官!
经过四个多月的坚持,《Seckill秒杀系统》终于接近尾声了,也感谢大家这四个多月以来的坚持和陪伴,也相信大家在《Seckill秒杀系统》专栏中,学到了不少知识和技术。接下来,我们就一起对《Seckill秒杀系统》专栏做个总结。
169 3
五个月,秒杀,38个大的篇章,126+篇文章、视频、小册,150+源码分支,完美收官!
【软工视频】第七章软件维护
【软工视频】第七章软件维护
如果只读一本与游戏设计有关的书,那一定就是这本了
在开始之前,先看一下作者 Jesse Schell 在前言中的忠告: 不要以为读了这本书或者任何一本书就能把你变成游戏设计师,更别提想当优秀的游戏设计师了,游戏设计不是一套原理而是一种活动,光读书当不了歌手,飞行员,篮球选手,也不能变成游戏设计师。要成为游戏设计师,只有一条路,那就是设计游戏,更精确点是设计别人真正喜爱的游戏,也就是说随便把游戏创意写下来是不够的,你一定要造出游戏亲自来玩,并让别人也来玩儿,如果感觉不满意(不会满意的),就要修改,修改,再修改,修改十几次,直到创作出大家确实爱玩的游戏为止。
83 0
|
存储 消息中间件 缓存
DDIA 读书分享 第五章:Replication,主从
DDIA 读书分享 第五章:Replication,主从
138 0
DDIA 读书分享 第五章:Replication,主从
|
存储 监控 NoSQL
DDIA 读书分享 第五章:Replication,复制滞后问题
DDIA 读书分享 第五章:Replication,复制滞后问题
96 0
DDIA 读书分享 第五章:Replication,复制滞后问题
|
消息中间件 XML 存储
DDIA 读书分享 第四章:编码和演化
DDIA 读书分享 第四章:编码和演化
156 0
DDIA 读书分享 第四章:编码和演化
|
JavaScript 前端开发 网络协议
《DDIA 逐章精读》小册
《DDIA 逐章精读》小册
432 0
|
算法
每日一题冲刺大厂第十三天 cow
大家好,我是泡泡,给大家带来每日一题的目的是为了更好的练习算法,我们的每日一题这个月进度是数据结构,让大家练到各种各样的数据结构题目,熟悉数据结构的增删改查,一年以后,蜕变成为一个不一样的自己!
83 0
|
存储 缓存 运维
金鱼哥RHCA回忆录:DO447构建高级作业工作流--使用事实缓存提高性能
第十二章 构建高级作业工作流--使用事实缓存提高性能
238 0
金鱼哥RHCA回忆录:DO447构建高级作业工作流--使用事实缓存提高性能
|
运维 API
金鱼哥RHCA回忆录:DO447协调滚动更新--委派任务和事实
第五章 协调滚动更新--委派任务和事实
109 0
金鱼哥RHCA回忆录:DO447协调滚动更新--委派任务和事实
下一篇
DDNS