考虑Redis Sentinel在网络分区时候的情况,这时Redis集群被网络分成两部分,Redis Sentinel在的大区域可能会提升Slave node作为primary node。如果这时候一直client在连接原来的primary node,这时会出现两个primary node(split-brain problem,脑裂问题)!也就是说,Redis Sentinel并没有阻止client连接Old primary node。在此时,已经连接到old primary node的client会写入old primary node,新的client会写入到new primary node。此时,CP系统已经完全瘫痪。虽然Redis集群一直是保持运行的,但是因为依赖于Quorum来提升slave节点,因此它也不会是AP系统。
如果使用Redis作为Lock service,那么这个问题会成为致命问题。这会导致分区后同时可以有两个client获取同一个锁并成功,lock service必须是严格的CP系统,像Zookeeper。
如果使用Redis作为queue,那么你需要接受一个item可能会被分发零次、一次或者两次等等,大部分的分布式队列都保证最多只分发一次或者最少分发一次,CP系统会提确切一次的分发然后带来较高的延迟。你需要明确使用Redis作为队列服务的话必须要接受网络分区后队列服务可能导致的不稳定。
如果使用Redis作为database,那么可想而知,利用Redis Sentinel建立的database是不能称为database的。
最后,以目前的Redis来说,使用官方提供的组件它只能成为Cache。构建一个分布式的Redis前往WheatRedis。
MongoDB
MongoDB采用类似于Redis的集群方式,primary node作为单点写操作服务然后异步写入replication nodes。但是MongoDB内建了primary选举和复制状态机,这使得primary node失败后,整个MongoDB会进行交流然后选择一个合适的slave node。然后MongoDB支持指定primary node可以确认slave node已经把写操作写入log或者真正写入,也就是通过一定的性能损耗来换取更强的一致性当primary node失败后。
那么MongoDB是否可以认定为是一个严格的CP系统?还是与Redis类似的问题,在网络分区后,当primary node在小的分区里,大的分区里的node会选举产生一个新的primary node,而此时在分区的时候,这两个node是会同时存在的(这个没有问题),然后当分区恢复后,小分区里的old primarynode会把在脑裂期间的操作发送到new primary node,这时候可能会产生冲突!
那么如何面对这个问题?接受它,首先这个冲突的概念像2PC一样可以在client端解决,同时MongoDB目前有WriteConnern可以解决这个问题,但会造成巨大的性能影响。
Dynamo
Dynamo是在传统的primary-slave模式遇到问题时候出现的红宝书,借鉴Dynamo的产品在一段时间内出现的非常多。
之前提到的系统都是面向CP的,起码是面向CP设计的。Amazon设计的Dynamo鲜明地面向AP。在Dynamo,它是天然地分区友好型,每一个node都是平等的,通过NWR来指定不同地一致性级别和可用性。这里不会详细阐述Dynaomo的原理(Dynamo,每一个试图了解分布式系统的人都应该对Dynamo这篇论文非常熟悉,即使它面临很多问题,但是论文中出现的对Dynamo设计的思考和变迁是宝贵的。
那么当分区发生时,Dynamo发生了什么?首先根据NWR的推荐设定(W+R>N),小区是不能得到新的写操作,新的对象会写在大区。然后在分区恢复后,小区的对象会滞后并与新的对象发生冲突。这里的冲突解决策略非常多,如Cassandra使用的client timestamps,Riak的Vector clock,如果无法解决,冲突可能会硬性覆盖或者推到业务代码。
然后Dynamo本身没有任何方法来判断一个节点是否数据同步,也无法判断,只能通过完全的数据比较,而这个过程是代价昂贵并且不灵活的。因此Dynamo提到说(W+R>N)可以达到强一致性是不可能的,故障节点只会是最终一致性。
因此,解决Dynamo的问题像前面一样,接受它。首先你的数据可以设计成immutable,然后你的数据决定可以在罕见情况下丢弃或者变旧,再或者使用CRDTs来设计你的数据结构。无论如何,Dynamo始终是一个good idea并且它推动了分布式设计的发展。
BigTable
上面提到的系统都是面向分布式的,要么AP要么CP。那么Bigtable是AC系统,虽然我们介绍的一直是分区问题,但是我们也需要考虑在中心化设计的Bigtable。无论是HBase还是HDFS都是这类设计,它们回避了分区问题并且在单IDC下达到非常好的效果。这里不会详细讨论中心化设计,因为它根本就没有考虑分区问题。
分布式数据库系统的思考
通过上述的分析可以了解到构建一个分布式数据库集群的困难,无论是同步复制,异步复制,Quorum还是其他的,在网络分区面前,任何挣扎都是无力的,网络错误意味着”I don’t know” not “I failed”。
构建一个“正确的”分布式数据库系统通常在几个方面达成意见: 1. 接受罕见的问题 2. 使用开源的软件,分布式系统会产生极大的“漩涡”在“理论正确的分布式算法”和“实际使用的系统“。一个有Bug的系统但是正确的算法比一个错误的设计更能接受。 3. 利用问题进行正确的设计,如使用[CRDTs](http://pagesperso-systeme.lip6.fr/Marc.Shapiro /papers/RR-6956.pdf) 4. split-brain问题是分区的原罪,如何解决split-brain之后的遗产才是正确的解决方案
小结
如何在OpenStack上做到HA是OpenStack官方和其他发行版公司都在努力的方向,而其中关键就在于数据存储的HA和一致性,在这个方向上,我们通过对”网络分区“这一关键问题的分析并在不同类型的数据库上进行落地思考,可以得到如何在其上规避、解决和接受它。通过在OpenStack 的产品上思考这些问题,我们可以在HA Solution上有更强健的基础。
原文转自:http://os.51cto.com/art/201307/403298.htm