分布式系统多副本复制的问题与解决方案

单主复制、多主复制和无主复制介绍

https://vonng.gitbooks.io/ddia-cn/content/ch5.html
https://zhuanlan.zhihu.com/p/105384140
https://zhuanlan.zhihu.com/p/100166494

单主复制的问题与解决方案

基于主库的复制要求所有写入都由单个节点处理,但只读查询可以由任何副本处理。所以对于读多写少的场景(Web上的常见模式),一个有吸引力的选择是创建很多从库,并将读请求分散到所有的从库上去。这样能减小主库的负载,并允许向最近的副本发送读请求。
在这种伸缩体系结构中,只需添加更多的追随者,就可以提高只读请求的服务容量。但是,这种方法实际上只适用于异步复制——如果尝试同步复制到所有追随者,则单个节点故障或网络中断将使整个系统无法写入。而且越多的节点越有可能会被关闭,所以完全同步的配置是非常不可靠的。
不幸的是,当应用程序从异步从库读取时,如果从库落后,它可能会看到过时的信息。这会导致数据库中出现明显的不一致:同时对主库和从库执行相同的查询,可能得到不同的结果,因为并非所有的写入都反映在从库中。这种不一致只是一个暂时的状态——如果停止写入数据库并等待一段时间,从库最终会赶上并与主库保持一致。出于这个原因,这种效应被称为 最终一致性(eventually consistency)
“最终”一词故意含糊不清:总的来说,副本落后的程度是没有限制的。在正常的操作中,复制延迟(replication lag),即写入主库到反映至从库之间的延迟,可能仅仅是几分之一秒,在实践中并不显眼。但如果系统在接近极限的情况下运行,或网络中存在问题,延迟可以轻而易举地超过几秒,甚至几分钟。

因为滞后时间太长引入的不一致性会导致一些问题:

读己之写

现象:用户先提交的数据,再查询却消失不见。
原因:数据写入主库时,并没有复制到所有从库。查询时正好访问到还未复制数据的从库。
解决方案

  • 读用户可能已经修改过的内容时,都从主库读。这就要求有一些方法,不用实际查询就可以知道用户是否修改了某些东西。举个例子,社交网络上的用户个人资料信息通常只能由用户本人编辑,而不能由其他人编辑。因此一个简单的规则是:从主库读取用户自己的档案,在从库读取其他用户的档案。
  • 如果应用中的大部分内容都可能被用户编辑,那这种方法就没用了,因为大部分内容都必须从主库读取(扩容读就没效果了)。在这种情况下可以使用其他标准来决定是否从主库读取。例如可以跟踪上次更新的时间,在上次更新后的一分钟内,从主库读。还可以监控从库的复制延迟,防止对任意比主库滞后超过一分钟的从库发出查询。
  • 客户端可以记住最近一次写入的时间戳,系统需要确保从库为该用户提供任何查询时,该时间戳前的变更都已经传播到了本从库中。如果当前从库不够新,则可以从另一个从库读,或者等待从库追赶上来。
  • 如果同一个用户从多个设备请求服务,例如桌面浏览器和移动APP。这种情况下可能就需要提供跨设备的写后读一致性:1.记住用户上次更新时间戳的方法变得更加困难,因为一台设备上运行的程序不知道另一台设备上发生了什么。元数据需要一个中心存储。2. 如果副本分布在不同的数据中心,很难保证来自不同设备的连接会路由到同一数据中心。 (例如,用户的台式计算机使用家庭宽带连接,而移动设备使用蜂窝数据网络,则设备的网络路线可能完全不同)。如果你的方法需要读主库,可能首先需要把来自同一用户的请求路由到同一个数据中心。

在这里插入图片描述

单调读

现象:用户两次查询数据,第一次查询的结果比第二次查询的结果更新。
原因:数据写入主库时,并没有复制到所有从库。第一次查询访问已经复制数据的从库,第二次查询访问还未复制数据的从库。
解决方法:确保每个用户总是从同一个副本进行读取(不同的用户可以从不同的副本读取)。例如,可以基于用户ID的散列来选择副本,而不是随机选择副本。但是,如果该副本失败,用户的查询将需要重新路由到另一个副本。
在这里插入图片描述

一致前缀读(因果倒置)

现象:用户先看到果,后看到因。
原因:在分区数据库中,因先写入分区主库,但是并未复制到所有分区从库。果后写入零一分区主库,且并未复制到所有分区从库。用户监听因分区从库和果分区从库,但是由于网络原因,果分区从库先成功复制数据,因分区从库后复制数据。所以用户先看到果,后看到因。
解决方法: 这是分区(partitioned)数据库中的一个特殊问题,解决方法未确保任何因果相关的写入都写入相同的分区。此做法等价于因果写入无分区的单主复制的分布式数据库,主库的因果写入的有序性和TCP传输的有序性,会保证从库的写入也是按照因果顺序。所以对于无分区的单主复制不会出现因果倒置的问题,但是在多主复制和无主复制中都是很常见的问题,所以多主复制和无主复制要解决因果排序问题。
在这里插入图片描述

多主复制和无主复制并发写入与因果写入

并发写入与因果写入

如果两个操作 “同时”发生,似乎应该称为并发——但事实上,它们在字面时间上重叠与否并不重要。由于分布式系统中的时钟无法完全同步的问题,分布式系统中是很难判断两个事件是否同时发生的。
在分布式系统中,为了定义并发性,确切的时间并不重要:如果AB两个操作都意识不到对方的存在,就称这AB两个操作并发。如果B的操作建立在A的操作上,则A和B不是并发,也可以说B是因果依赖(causally dependent)于A。因此,只要有两个操作A和B,就有三种可能性:A在B之前发生,或者B在A之前发生,或者A和B并发。

比如下图中因为Client B递增的值是Client A插入的值,所以B因果依赖于A。
在这里插入图片描述
下图当每个客户端启动操作时,它不知道另一个客户端也正在执行操作同样的Key。因此,操作之间不存在因果关系。
在这里插入图片描述

因果关系倒置解决方案

版本向量(向量时钟)
https://blog.csdn.net/qq_41775852/article/details/104933728
针对数据库中同一个key的记录的具有因果关系的写入,果写入可以覆盖因写入。因为果写入建立在因写入之上,不会存在冲突问题。

并发写入冲突解决方案

不具有因果关系的写入就是并发写入。由于并发写入是针对于同一个key的记录的独立的修改,所以会引起冲突。所以需要合并冲突,一般有以下方法:

  1. 给每个写入一个唯一的ID(例如,一个时间戳,一个长的随机数,一个UUID或者一个键和值的哈希),挑选最高ID的写入作为胜利者,并丢弃其他写入。如果使用时间戳,这种技术被称为最后写入胜利(LWW, last write wins)。虽然这种方法很流行,但是会造成数据丢失。比如多个用户同时更新一个账户存款,在原有的存款上增加100,两用户并发修改导致账户本来应该增加200,却只有一个修改生效增加100(这是并发写入,因为两个修改操作基于原始数据,而不依赖对方修改后的数据)。这在单主数据库中可以使用悲观锁或者乐观锁解决,但是在多主数据库中加锁需要与所有主库通信,这就失去了多主数据库读取写入的性能优势,多主也就失去了意义(文档协同编辑是一种特殊的多主数据复制,是利用悲观锁锁住各用户编辑的单元格,因为其更强调一致性而不是高可用。)。
  2. 以某种方式将这些值合并在一起。比如:多个值连接变成一个值或者多个值均存储下来(提供给用户自行选择)或者自定义冲突解决逻辑。

合并冲突的解决方案决定了数据的并发写入的一致性保证的强弱。

多主复制和无主复制的问题和解决方案

多主复制并发写入冲突

原因:两个客户端同时独立无因果依赖的更新同一条记录,但是访问的是不同的主库。当主库把数据复制到其他主库时,发现此条记录出现冲突。
解决方案:合并冲突,如上
在这里插入图片描述

多主复制因果写入倒置

现象:客户端A向主库1的表中插入一行,客户端B在主库3上更新该行。然而,主库2可以以不同的顺序接收写入:它可以首先接收更新(其中,从它的角度来看,是对数据库中不存在的行的更新),并且仅在稍后接收到相应的插入(其应该在更新之前)。
解决方法:利用版本向量,捕获两个写入操作的因果性,果写入覆盖因写入。
在这里插入图片描述

无主复制的并发写入冲突

原因:与多主复制类似,两个客户端同时独立无因果依赖的更新同一个记录,但是访问的是不同副本(无主复制中没有主库、从库,所有的都是副本)。
解决方法:合并冲突,如上
在这里插入图片描述

无主复制的因果写入倒置

原因:与多主复制类似,客户端A更新副本1中的记录,副本1同步数据到副本3。客户端B读取副本3的数据,再次更新该数据。两次更新同步到副本2上时,出现因果倒置。
解决方案:版本向量,果写入覆盖因写入。

总结

对于单主复制的数据库不存在并发写入冲突以及因果写入倒置的问题,因为所有的数据写入都通过同一个leader进行排序,TCP传输也保证了数据写入按照顺序复制给所有副本。对于多主复制以及无主复制的数据库,因为数据可以通过不同的主库或者副本写入,所以或造成并发写入冲突以及因果写入倒置。并发写入冲突的解决方案是按照一定方法合并冲突,因果写入倒置的解决方案是利用版本向量为因果写入排序,果写入覆盖因写入。
数据复制还分为同步复制和异步复制,同步复制保证强一致性,但是可用性较差,异步复制保证高可用性,但是一致性较弱。最佳的多副本复制方案应该是使用Paxos/Raft,可实现较高可用性+较强一致性。

参考:《设计数据密集型应用》

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值