最终一致性与CAP中CP模式的对比

最终一致性与CAP中CP模式的对比

 
最终一致性(Eventual Consistency/EC)是众所周知的概念。CAP理论同样也被大家所了解,它定义了一致性(Consistency)、可用性(Availability )和分区容错性(Partition tolerance),它描述了某些分布式系统的特性,比如:CP类型分布式系统具有两个特性:一致性与分区容错区。
 
 
如果我们着眼于数据存储如何遵守自己的一致性模型,并比较它们的速度,哪一种会是最快的? EC(最终一致性)或者CP存储? 回答这个问题将是一个很好由头儿,让我们探究它们各自的定义、展现它们的一些局限性。
 

与流行的观点相反, 数据存储可以非常快
 

最终一致性

最终一致性由Werner Vogels定义于【E2】:“存储系统保证,若对象没有新的更新,最终所有的访问都将返回对象最后的更新值”
 
让我们尝试一个假想key-value存储的一些实现。

实现1:尝试最简单的选择

void put(key, value){
// 什么也不做
}
 
value get(key){
// 嘿, 很容易实现! 我假装没有收到写请求,这就足够了!
throw “no value for this key”
}
 
这是一个最终一致性存储? 不,它不是,因为它从不会返回最后的更新值。要成为一个最终一致性存储,需要“最终[......]返回最后的更新值”。这是此实现没有做到的,它永远不会返回最后的更新值。
 
让我们尝试另外一个实现:

实现2:试图快速读取

void put(key, value){
doRealPut(key, value) // 真正地干活儿
}
 
value get(key){
if (random(2) == 1) // 50%的时间
throw “no value for this key”
else
return doRealGet(key)
// 50%的时间,我假装没有收到前一次的插入,两次返回一次值。
}
 
这是一个最终一致性存储? 再次:它不是。因为没遵守这个属性:“最终所有的访问将返回最后的更新值” 。不满足条件”所有的访问“。
 
不管怎样也不能阻止我们干可笑的事情,比如:

实现3:玩个文字游戏

void put(key, value){
doRealPut(key, value)
}
 
value get(key){
if (currentDate.year < 2020)
throw “no value for this key”
else
return doRealGet(key)
// 我在2020年以前将赢得所有的读取benchmarks测试。
}
 
上面最后这个实现符合定义,但它是在玩文字游戏。在日终时,我们必须返回最新值的事实很重要,因为我们不能欺骗可用性。
 
最后,一种强实时存储实现,具有严格的SLA与“使它在预期时间内返回或者中止”策略,不符合最终一致的定义:

实现4:尝试最终一致性的强实时系统

void put(key, value){
doRealPut(key, value)
}
 
value get(key){
try (timeLimit = 10 ms) { // 在10毫秒内返回或抛出
return doRealGet(key)
} catch (TimeOutException) {
// 最大响应时间是10毫秒!
throw “no value for this key”
}
}
 
这种行为有时很管用。然而,一个最终一致性的存储不被允许这样做:它打破了一致性契约。就象第二个实现,“最终所有的访问将返回最后的更新”的承诺被打破。
 
不过,此行为可用于任何存储,最终一致性或不是。实际上,可以认为在许多存储上都有设置类似超时的选项(再次,最终一致性或不是)
 

CAP与CP

如果你正在阅读本文,你已经看到了CAP及其定义:”一致性(Consistency)、可用性(Availability)、分区容错性(Partition tolerance):选取其中两个“。CAP是由Eric Brewer首次在2000年提出的一个猜想【C1】,在2002年,由Seth Gilbert 和Nancy Lynch证明【C2】。
 
让我们使用被证明过的定义【C2】:
一致性(Consistency):“原子性,可串行,一致性【...】。所有操作必须存在一个整体上的顺序,这样每个操作看起来好像是在一个瞬间内完成。这相当于要求,分布式共享内存系统对请求的处理,表现得就像它们在单个节点上执行一样,一次一个地响应操作”。
 
可用性(Availability):“一个分布式系统持续可用,被非故障节点收到的请求必须有响应 ”
 
分区容错性(Partition tolerance):“允许网络丢失任意多个从一个节点发送到另一节点的消息。当网络分区发生时,从一个分区中节点发向其它分区中节点的所有消息将丢失”。
 
这让我们有一个CP系统的简单实现:

实现1:CP做起来很简单

void put(key, value){
throw “not available”
}
 
value get(key){
throw “not available”
}
 
这个“什么也不做”的实现,完全符合CP的定义:它不完全可用--实际上根本不可用--但它是一致性的。实际在【C2】中提到:“如果不要求可用性,那么很容易实现原子性和分区容错性。这个简单的系统忽略所有的请求,来满足这些条件要求”。
 

CAP与AP

AP存储有所不同吗? 不见得:CAP理论没有要求AP实现一致性,那怕是最低限度的一致性。【C2】提到“如果不要求原子一致性,它可以提供高可用性和分区容错性。如果没有一致性要求,服务可简单地对每个请求都返回v0(一个初始值)”。 这意味着,我们可用非最终一致性的最小实现,来做一个具有可用性与分区容错性的存储:

实现2:AP也很容易

void put(key, value){
// do nothing
}
 
value get(key){
throw “no value for this key”
}


是一个有用处的CP存储吗?
 

结论

对于任何合理的数据存储实现,最终一致性的定义导致一个非常明确的行为。而CAP理论中的AP和CP定义更为宽松,并允许构建无用的,但完全地CP或AP存储。
 
一个直接的推论:任何一个在分区产生时最终崩溃的简单应用,可获得CP-分区容错性奖项。
 

参考

[C1] Eric A. Brewer, PODC Keynote, July 19, 2000, Towards Robust Distributed Systems
[C2] Gilbert and Lynch. Brewer’s conjecture and the feasibility of consistent, available, partition-tolerant web services. ACM SIGACT News (2002)
[E2] Werner Vogels, Eventually Consistent, ACM Queue, Vol.6 No.6, 2008

猜你喜欢

转载自jj-tyro201504151529.iteye.com/blog/2203440