最近上分布式与并行系统课有感,根据课上内容加以总结。
一致性模型
一致性模型并没有绝对正确或错误的,而是在易于编程性和效率之间的权衡
在分布式系统中实现一致性的挑战:
- 数据复制(缓存)
- 并发(无共享时钟)
- 故障(机器或网络)
常见一致性
顺序一致性(Sequential consistency):保证每个人都看到相同的读写顺序,所有读/写操作都遵循某种总排序,且读操作获取最新的写操作结果
发布一致性(Release consistency):每个人都按锁的释放顺序看到写入
两种一致性的缺点:
-
速度非常慢,每次操作前必须询问,顺序一致性每次读写操作需要询问主控,而发布一致性需要询问锁管理器是否可以获取或释放锁。要求高可用的连接,核心管理器不能挂。
-
不适合某些特定场景,比如客户端断开连接时会影响一致性;购物车等应用程序可能更关注可用性,而允许不影响关键服务的不一致;地理位置较远的应用程序。
最终一致性方案:Bayou
论文:Managing Update Conflicts in Bayou, a Weakly Connected Replicated Storage System (SOSP’95)
最终一致性目标:更好的性能,确保:
- 在对数据排序之前能够接受写入
- 读取可能会返回旧值,但不会阻塞直到最新值出现
可能出现的问题:
-
写写冲突
-
读取丢失因果关系的旧值
举个例子,有个照片应用拥有保存各用户上传照片的数据库,该应用允许用户增删照片。首先该数据库通过分布式的server集群进行多备份保存,从而防止某台机器crash导致数据完全丢失的情况。
在这种情况下,同个用户的电脑client端和手机端同时传不同的照片,就会出现写写冲突,即不同的服务器对于这两张照片的出现顺序可能不同。
对于这种情况,Bayou提出采用update函数,其读取存储的当前状态,决定正确的更改顺序,通过有序更新日志,来确保两个节点在日志中具有相同的更新。
其设计为Update ID:<时间 T,节点 ID> ,其由创建update函数的节点分配。
对update函数X和Y执行顺序的规则为:X < Y iff (X.T < Y.T) 或 (X.T=Y.T 且 X.ID < Y.ID)
而对于时钟时间,如果不同机器的时钟不同是否会对更新顺序一致性产生影响?
比如client在SRV1时钟为10的时候创建一个添加图片的操作,而SRV2得到同步后手机执行删除图片的操作,这时候对于SRV2来说其时钟为9,那么SRV1同步时就会出现删除不存在资源的操作。
论文给出解决方案:
使用Lamport 逻辑时钟:
- 每个服务器都保留一个时钟 T
- 根据实际时间的推移,每秒递增 T一秒
- 如果从另一台服务器其时钟 T’,则修改 T = Max(T, T’+1)
就刚才上述情景,SRV2同步SRV1的操作后看到T’为10,所以会更新T=Max(T, 10+1)=11,所以删除操作的时间会变为13。
接着又会有问题,对于一些图片,在某台服务器上执行的更新与其他服务器发来的更新冲突,此时服务器发现在自己的更新操作前还执行了一堆更新而没有同步,此时的冲突需要大量的回滚,而它不知道要回滚多少,所以要通过完整的log记录来重新执行更新,这样会时操作变得复杂。
所以论文提出指定一台服务器指定为主服务器,通过其确定总的分配提交顺序,使用全局的CSN(Commit Sequence Number)加入时间戳:<CSN, local-TS, SrvID> ,对于任何具有CSN 的写入均认为是stable的,这些stable的写入不会被回滚,而且CSN 定义提交更新的总顺序,可以在服务器之间交换。
广域存储下的因果一致性:COPS
论文:COPS: Scalable Causal Consistency for Wide-Area Storage
这里说明一下因果一致性:
- 如果有 op1 和 op2 在单个执行线程中,并且 op1 在 op2 之前发出,则op1 -> op2 (Thread-of-Execution)
- 如果 op2 读取了 op1 写入的结果,则op1 -> op2 (Get-from)
- 如果op1 -> op2, 且op2 -> op3,则op1 -> op3 (Transitivity)
COPS主要针对于广域存储,不同地区通过长距离的链路连接的多个数据中心,这是当前普遍的情况。 其觉得相比之下,Bayou已经实现了Availability、Low Latency 、Partition Tolerance和Causal+ Consistency(即Causal + Conflict Handling),但是Scalability并不佳。而这是由于Log的设计导致的,原因如下:
- 日志是单一的序列化,其隐式获取并强制执行因果顺序。
- 一个节点存储所有内容,为集群中的所有操作提供日志,大大限制可扩展性。
- 对于跨区的站点,并不存在因果关系。
而论文认为实现Scalability的关键在于通过元数据显式获取因果关系,通过分布式验证取代单一序列化,通过显式的依赖性传播来同步因果关系,比如:站点 A 执行写入,将该写入与其依赖关系一起复制到另一个站点 B,则站点 B 会先等待,直到写入的依赖性均满足,然后再提交该写入。
比如当client1存数据时,其本地会保存一个依赖关系deps,其会向服务器发送put_after请求会带deps参数,然后会返回一个Key的版本保存在deps中,通过版本号来体现Thread-of-Execution的规则,而除此之外由于Get-from和Transitivity原则还需要另外两个依赖,当put_after操作发到服务器时,会先检查这些依赖,只有满足这些依赖才能写入数据。而当所有数据中心提交上个的值之后,经过一段时间可以将依赖删除,以避免依赖过多。
COPS 是一个可扩展的分布式存储系统,在不牺牲 ALPS 属性的情况下提供因果+一致性,通过在每个集群中在写入之前检查因果依赖关系是否得到满足,来实现因果+一致性。