一致性 (Consistency)(等同于所有节点访问同一份最新的数据副本)
可用性(Availability)(对数据更新具备高可用性)
网络分区容忍性(Partition tolerance)(以实际效果而言,分区相当于对通信的时限要求。系统如果不能在时限内达成数据一致性,就意味着发生了分区的情况,必须就当前操作在C和A之间做出选择。)
根据定理,分布式系统只能满足三项中的两项而不可能满足全部三项。
理解CAP理论的最简单方式是想象两个节点分处分区两侧。 允许至少一个节点更新状态会导致数据不一致,即丧失了C性质。 如果为了保证数据一致性,将分区一侧的节点设置为不可用,那么又丧失了A性质。 除非两个节点可以互相通信,才能既保证C又保证A,这又会导致丧失P性质。
这个定理起源于加州大学伯克利分校(University of California, Berkeley)的计算机科学家埃里克・布鲁尔在2000年的分布式计算原则研讨会 (Symposium on Principles of Distributed Computing(PODC))上提出的一个猜想。 在2002年,麻省理工学院(MIT)的赛斯・吉尔伯特和南希・林奇发表了布鲁尔猜想的证明, 使之成为一个定理。 吉尔伯特和林奇证明的CAP定理比布鲁尔设想的某种程度上更加狭义。 定理讨论了在两个互相矛盾的请求到达彼此连接不通的两个不同的分布式节点的时候的处理方案。From:WikiPedia.org
当我第一次读完WikiPedia的解释是似懂非懂的,现在懂了觉得WikiPedia这个条目的撰写者真是字字珠玑。
我们以数据库为例,讨论一下CAP原理。
首先,我们讨论一种最简单的情况:"单数据库实例"。 这也是最常见的小站点、个人博客、小论坛的架构。
我们可以很容易分析出来,由于单实例,所以不存在“网络分区”、“不一致”, 但单点故障后会导致整个数据库瘫痪,所以可用性不能保证。
这就是CAP定理中的,保证"C"和"P",舍弃"A"。
所有单机版的系统都属于这个范畴,例如MySQL、memcached、redis。
Sharding可以翻译为分片。
为了提升可用性,我们在实际生产环境下经常会在客户端应用一些哈希算法,进行数据分片存放,如下图所示:
由于数据是分片存储在每个数据库中,所以依旧能保证数据一致性。
由于数据库之间没有互相通信,并不依赖彼此的存在,所以分区可容忍性依旧没有破坏。
那么可用性呢?很多时候会有人直接拍脑袋,这里我们用数学的方式来解答这个问题。
假设,集群有两台服务器,数据分布均匀,我们数据库实例宕机的概率是p。 那么这种利用哈希进行数据分片的集群的可用性为:
即使,数据分布均匀或者集群数量增大,结果也是一样的:“集群可用性依旧为p”。
那我们折腾了半天,CAP和单机竟然是一样的,为了个球啊? 这种情况下CAP各项指标虽然没有提升,但好处是:
单个服务器宕机只会导致服务降级;
集群有了扩容缩容的可能性,这就叫做scalability。
这种分布式的方式常用于:
分布式memcached、redis
传统的数据库Sharding
BigTable (列存储式数据库)
Hypertable (列存储式数据库)
HBase (列存储式数据库)
MongoDB (文档式数据库)
Terrastore (文档式数据库)
Redis (KV数据库)
Scalaris (KV数据库)
MemcacheDB (KV数据库)
Berkeley DB (KV数据库)
如果我们想要保证更高的可用性,那应该怎么办呢?我们就有了下面的做法:
Client多副本写入,就是Client在写数据库的时候对多个数据库进行写入,并且在两个都写入成功后才认为成功。 由于数据存在多个副本,这种方式会大大的提高读取的可用性。但由于写入的时候要多写, 副本所在的所有实例都必须可用才能成功。所以写入的可用性反而下降了。
假设单机数据库的故障率为p(p<1.0),那么单机数据库的可用性为1-p。 总结就是:
在写入的场景下,一致性( C )和分区可容忍性( P )没有变化,可用性( A )反而有所下降, 从1-p降低到1-2p-p2
在读取的场景下,一致性( C )和分区可容忍性( P )依旧没有变化,可用性( A )有所上升, 从1-p上升到1-p2
我们可以进一步得到结论:
"Client多副本写入"这种写入方式非常适合于在"读多写少"的场景下提高可用性。
为了改善写入时糟糕的可用性,这种方式还有一些“变种”,例如:
写成功部分副本就返回成功,剩下的副本写入不保证结果。这样做的结果就是牺牲一定的一致性( C ),换取可用性( A )的提升。
总的来说,这种方式属于广义的"Sharding"的范畴,除去上述的缺点还有一个较大的问题就是:
假设副本数为n,Client写入单实例的耗时为t,多副本写入的耗时就是n*t;当n > 1的时候会成倍的影响Client的写入性能。
为了解决Client写入慢调用复杂等问题,我们引入了集群方案,也就是Clustering。 Clustering和Sharding对比如下:
多副本模式:
我们可以看到,由于多个副本写入成功才返回,这种方式一致性( C )依旧是保证的。 但写入可用性( A )和分区可容忍性( P )相对于单机均会下降。换来的是:
较为简单的API,客户端不用关注“多写”问题;
读取操作的高可用(HA)。
由于上述方案是强一致性( C )的,这种应用场景常见于金融系统,这种这方面典型的代表有:
ZooKeeper (KV数据库)
Vertica (列存储式数据库)
Aster Data (关系型数据库)
Greenplum (关系型数据库)
类似"Sharding"中我们采用的方案,生产环境线上的数据库也往往采用放弃一定的一致性( C ) ,来提高可用性( A )和分区可容忍性( P )。
如下图:
可以看到,由于大多数互联网公司的需求不是要求强一致性( C ), 所以通过放弃一致性,达到更高的可用性( A )和分区可容忍性 ( P )成了目前市面上大多数NoSQL数据库的核心思想。
在Amazon著名的分布式数据库Dynamo中,就是采用类似的方法:"3副本,写入2个成功后就返回成功,剩下的1个副本后续再进行同步",我们称这种模式叫"最终一致性"
这方面典型的代表还有:
Dynamo (KV数据库)
Voldemort (KV数据库)
Tokyo Cabinet (KV数据库)
KAI (KV数据库)
Cassandra (列存储式数据库)
CouchDB (文档式数据库)
SimpleDB (文档式数据库)
Riak (文档式数据库)
MooseFS (类GFS分布式文件系统)
基本上,上面几种情形已经可以涵盖所有分布式系统的情形了。 我们可以看到,系统设计就像穷人家的被子,盖住头和左脚就露出右脚,盖住头和右脚就露出左脚…… 即使你再有钱也不可能将CAP同时100%满足。
那么问题就来了,有没有可能将CAP同时提升呢?答案是:Sure, ofcourse.
我们可以通过用更高可靠性的服务器、更可靠的网络设备达到CAP同时提升。
例如,假设某银行刚开始采用的是2w块钱的Dell服务器,由于银行业务的特殊性,一致性( P )是首要保证的(任何银行都不愿意看到,同时在ATM上取账户里的100块,都成功了)所以这个服务器可能就是一个单点,可用性( A )十分差劲。银行有钱了,服务器换成IBM大机,CPU都是双路热备份。可用性( A )自然非2w的Dell能比的。
但还是那句话:
就算你再有钱,要找不到一个能盖住双腿和头的被子。
我们知道,数据库的事务有ACID的保证:
事务机制可以确保数据一致性。
事务应该具有4个属性:原子性、一致性、隔离性、持久性。这四个属性通常称为ACID特性。
原子性(atomicity)。一个事务是一个不可分割的工作单位,事务中包括的诸操作要么都做,要么都不做。
一致性(consistency)。事务必须是使数据库从一个一致性状态变到另一个一致性状态。一致性与原子性是密切相关的。
隔离性(isolation)。一个事务的执行不能被其他事务干扰。即一个事务内部的操作及使用的数据对并发的其他事务是隔离的,并发执* 行的各个事务之间不能互相干扰。
持久性(durability)。持续性也称永久性(permanence),指一个事务一旦提交,它对数据库中数据的改变就应该是永久性的。接下来的其他操作或故障不应该对其有任何影响。
后来,随着NoSQL的兴起,技术界又提出了BASE的概念:
Basically Availble --基本可用
支持分区失败(Sharding碎片划分数据库),出了问题服务仅降级(部分不可用)。
Soft-state --软状态/柔性
事务"Soft state" 可以理解为"无连接"的, 而 "Hard state" 是"面向连接"的。 软状态就是可以有一段时间不同步,异步。
Eventual Consistency --最终一致性
最终数据是一致的就可以了,而不是时时一致。
合起来就是BASE。
比较有意思的是:在英语里ACID是酸的意思;BASE也有碱的意思。
◆著作权归作者所有◆
商业转载请联系作者获得授权非商业转载请注明出处。
作者大家可以看我的知乎专栏
链接http://zhuanlan.zhihu.com/auxten/20315482
==================================
》》想要了解更多精彩内容欢迎关注》》
联系Reboot-有更多技术分享、交流请加群:238757010