集群内数据一致性算法

关于集群内数据的一致性

我们通过Quorum 和Clock 算法来具体讲解一下,亚马逊Dynamo 的论文中对Quorum 和Clock 有比较详细的介绍。

Quorum

先来看Quorum ,它是用来权衡分布式系统中数据一致性和可用性的,我们引入三个变量,如下。

N :数据复制节点数量。

R :成功读操作的最小节点数。

W :成功写操作的最小节点数。

如果W +> N ,是可以保证强一致性的,而如果W +R≤N ,是能够保证最终一致性的。

根据前面的CAP 理论,我们需要在一致性、可用性和分区容忍性方面进行权衡。

例如,如果让W =N 且R =1 ,就会大大降低可用性。

Vector Clock

Vector Clock 的思路是对同一份数据的每一次修改都加上< 修改者,版本号> ”这样一个信息,用于记录修改者的信息及版本号,通过这样的信息来帮助我们解决一些冲突。



Vector Clock 背景

假设有如下场景:Alice 、Ben 、Catby 和Dave 四人约定下周要一起聚餐,四个人通过邮件商量聚餐的时间。Alice 首先建议周三聚餐。

之后Dave 和Catby 商量觉得周四更合适。

后来Dave 又和Ben 商量之后觉得周二也行。

最后Alice 要汇总大家的意见,

得到的反馈如下:

Catby 说,他和Dave 商量的时间周四。

Ben 说,他和Dave 商量的时间是周三。

此时恰好联系不上Dave ,而且不知道Catby 和Ben 分别与Dave 确定时间的先后顺序。

Alice 就不能确定到底该定在哪一天了。

类似的事情经常会发生。当你向两个或几个人问一些消息时,返回的内容往往不一样,而且你不知道哪个是最新的。

Clock 原理

Clock 就是为了解决这种问题而设计的,简单来说,就是为每一个商议结果附上一个时间戳,当结果改变时,更新时间戳。加上时间戳后,我们再一次描述上面的场景,如下。

当Alice 第一次提议将时间定为周三时,可以这样描述这个信息:    

data = Wednesday     

vclock = Alice:1 vclock

就是这条消息的Clock ,Alice:1 表示这是从Alice 发出的第一个版本。接着,Dave 和Ben 商量将时间改为周二,Ben 发给Dave 的消息如下:    

data = Tuesday     

vclock = Alice:1 ,

注意Ben 这条消息保留了Alice 的记录,同时加上了自己的记录。Ben:1 代表这是Ben 第一次修改的记录。

接着Dave 收到Ben 的消息,并同意将时间改为周二,他回给Ben 的消息如下:    

data = Tuesday     

vclock = Alice:1, Ben:1, Dave:1

这条消息同样保留了原来已有的vclock 记录,同时加上了自己的记录。

另一方面,Catby 收到Alice 的消息,打算与Dave 商量将时间改为周四,于是他发送如下消息给Dave:   

data = Thursday     

vclock = Alice:1, Catby:1

看到这里你可能会奇怪,为什么vclock 中没有了之前的Ben 和Dave 的记录了?

这是因为Ben 和Dave 商量的时候Catby 并不知道这个情况。Catby 手中的信息还是Alice 最初发送的那份。这样当Dave 收到来自Catby 的消息时就发现有冲突了。

Dave 手中的两份信息如下:    

date = Tuesday     

vclock = Alice:1, Ben:1, Dave:1     

date = Thursday    

vclock = Alice:1, Catby:1 Dave

通过比对两份消息的vclock 可以发现冲突,这是因为上面两个版本的vclock 都不是对方的“祖先”。

其中clock 对祖先的定义是这样的:

对于A 和B ,当且仅当A 中的每一个标记ID 都存在于B 中,同时A 中对应的标记版本号要小于等于B 时,vclockA 才是vclockB 的祖先。

如果标记ID 不存在,可以认为标记版本号为0 。

Dave 通过对比vclock 发现了版本冲突,于是尝试解决冲突。

两个版本中只能选择一个,他选择了时间为周四的,那么这条消息可以表示为:    

date = Thursday     

vclock = Alice:1, Ben:1, Catby:1, Dave:2

Dave 在vclock 中加上了两个消息中的全部标记ID (Alice 、Ben 、Catby 和Dave ),同时将自己对应的版本号加1 ,然后将这条消息发送给Catby 。最后,当Alice 从Catby 和Ben 收集反馈消息时(此时Dave 联系不上),收到如下消息来自Ben 的:    

data = Thursday     

vclock = Alice:1, Ben:1, Dave:1

来自Catby 的:    

data = Thursday     

vclock = Alice:1, Catby:1, Ben:1, Dave:2

这时Alice 从Catby 的消息就可看出,Dave 后来改变主意了。

建议

到这里,我们来介绍一些分布式环境下的与事务相关的算法和实践。从工程上来说,如果能够避免分布式事务的引入,那么还是避免为好;如果一定要引入分布式事务,那么,可以考虑最终一致的方法,而不要追求强一致。而且从实现上来说,我们是通过补偿的机制不断重试,让之前因为异常而没有进行到底的操作继续进行,而不是回滚。如果还不能满足需求,那么基于Paxos 算法的实现会是一个不错的选择。