本文是《如何学习分布式系统》中,关于一致性模型的相关介绍。
在《Session Guarantees for Weakly Consistent Replicated Data》一文中,作者定义了在弱一致性同步情况下的四种session内一致性,即分布式系统节点内部采用最终一致性进行同步,session则内部保持更高的一致性。在有些资料里,这些也叫做Client-centric consistency models。
定义和术语
假设系统由一系列server和client组成,server之间使用最终一致性进行同步,client则可能访问不同的server。
用DB(S,t)表示截止到t时刻,S接收到的写请求序列,DB(S)表示截止目前,S接收到的写请求序列。最终一致性保证了对每个写请求W,每个server S,存在时刻t,使得W存在于DB(S,t)。所有的server需要按照同样的顺序apply写请求。WriteOrder(W1,W2)用来确定是否W1应该排序在W2之前。
Session内一致性保证
Read Your Writes
写操作总是能够被同一个session内的读操作获取。
Read Your Writes:如果一个session内,读请求R在写请求W之后,并且R在t时刻被server S处理,那么W应该在DB(S,t)中。
如果没有这个保证,就会出现一个用户修改完密码之后,发现无法用新密码登录的情况。或者当他把email里的部分邮件删了以后,过了一会儿发现删掉的邮件又重新出现了。
Monotonic Reads
Monotonic Reads叫做单调性读,保证session内的读取操作只会读取越来越新的数据。
指定一个写操作集合WS,WS是DB(S,t)的子集,如果对DB(S,t)任意的子集WS2,其中WS2包含WS,对于读操作R来说,R应用到WS2的结果和R应用到DB(S,t)的结果一致,那么认为WS对R和DB(S,t)是complete的。用RelevantWrites(S,t,R)表示一个函数,该函数返回对R和DB(S,t) complete的最小的写操作集合。
Monotonic Reads:在一个session中,如果读操作R1在R2之前发生,R1在t1时刻访问server S1,R2在t2时刻访问S2,那么RelevantWrites(S1,t1,R1)是DB(S2,t2)的子集。
也就是说,在R2之前,对R1有决定性作用的写操作应该传播到S2。
假设没有Monotonic Reads,用户第一次读取了所有文字的标题,然后他点击一个标题来获取文章内容,这时发现文章不存在,这该多么尴尬。
Writes Follow Reads
一个session内写操作之前有读操作,那么读操作的效果要先于写操作传播。所谓“读操作的效果”,指的是对其有影响的写操作。比如session内有个读操作r,读取的数据来自于写操作w1,然后session内有个写操作w2,那么w2应该在w1之后被传播。注意这里w1不一定来自同一个session。
Writes Follow Reads:在一个session中,如果读操作R1在写操作W2之前,R1在t1时刻由server S1处理,那么对于任意server S2,如果W2在DB(S2)中,那么任意的在RelevantWrites(S1,t1,R1)中的写操作W1也会在DB(S2)中,并且WriteOrder(W1,W2)
这个保证了假设更新操作是基于数据x的,那么传播后的更新操作至少也能基于x或者更新的x。
例如有个写操作W1创建了一篇文章,R操作读取了文章内容,W2创建了文章的评论。那么显而易见,在其他server上,也应该先有W1,后有W2。
Monotonic Writes
Monotonic Writes叫做单调性写,保证session内的写操作必须保留顺序。
Monotonic Writes:在一个session里,如果写操作W1在写操作W2之前,那么对于任意的server S来说,如果W1和W2都在DB(S2),那么WriteOrder(W1,W2)
有了Monotonic Writes,保证了对文章版本N的修改会出现在版本N+1之前,否则版本N+1就会被版本N覆盖。
因果一致性的保证
按照很多地方的说法,实现了因果一致性的系统,就能保证以上session内一致性。例如wiki上就是这么解释的。
以下则是mongodb的解释:
https://docs.mongodb.com/manual/core/read-isolation-consistency-recency/#client-sessions-and-causal-consistency-guarantees
更多相关内容,请参考系列文章《如何学习分布式系统》。