关于ACID,BASE和CAP定理的探究
前言
当我看到”根据CAP理论,由于分布式系统必须保证分区容错性,所以只能选择AP原则或者CP原则“这种结论时,我感到很疑惑:
- 什么是分区容错性?
- 为什么分布式系统必须保证分区容错性?
- 为什么说Eureka这样的分布式系统属于AP原则,它明明没有完全放弃一致性啊?
- 真的存在不用考虑数据一致性的系统吗?AP和BASE有严格的区别吗?
为了解决上面的疑惑,我查了很多资料,在下面的文章中,我会一一回答上面提出的问题。
事务和ACID
ACID是传统关系型数据库事务的四个特性,其中的四个字母分别代表以下单词:
Atomicity, Consistency, Isolation, Durability
- 原子性(Atomicity): 指所有在事务中的操作要么都成功,要么都不成功,所有的操作都不可分割,没有中间状态。一旦某一步执行失败,就会全部回滚到初始状态。
- 一致性(Consistency): 在事务开始之前和事务结束以后,数据库的完整性约束没有被破坏。这表示写入的资料必须完全符合所有的预设约束、触发器、级联回滚等
- 隔离性(Isolation):数据库允许多个并发事务同时对其数据进行读写和修改的能力,隔离性可以防止多个事务并发执行时由于交叉执行而导致数据的不一致。事务隔离分为不同级别,包括未提交读(Read uncommitted)、提交读(read committed)、可重复读(repeatable read)和串行化(Serializable)。
- 持久性(Durability):事务处理结束后,对数据的修改就是永久的,即便系统故障也不会丢失
具有ACID特性的数据库系统,可以保证在写入或更新数据时,事务是正确可靠的。ACID的目标是保证数据的正确性和一致性。
从ACID到BASE
1997 年,Brewer提出BASE,他们的服务集群在架构上放弃了关系型数据库的 ACID 特性而使用BASE原则
BASE的四个字母分别代表以下单词:
Basically Available, Soft State, Eventual Consistency
- 基本可用(Basically Available):分布式系统在出现不可预知故障的时候,允许损失部分可用性
- 软状态(Soft State):允许系统中的数据存在中间状态,即不同节点上的数据不一致
- 最终一致性(Eventually Consistent):软状态不能一直持续,在一定期限过后,应当保证所有副本保持数据一致性,从而达到数据的最终一致性
BASE的思想是,为了提高系统的可用性,允许在某些时候,某个节点返回的不是最新的数据,但是在一段时间之后,系统中的数据最终会达到一致。这种做法可以在系统访问高峰时牺牲一定的一致性,保证可用性,在高峰过后又恢复数据的一致性。
可以看出,ACID和BASE两种特性是对立的。
ACID的特点是追求数据的一致性和正确性,但是这种严格的要求牺牲了系统的可用性(在数据的不同副本达到一致之前,这份数据都处于不可用的状态)。而BASE则是在一定程度上容忍了数据出现暂时的不一致,从而提高了系统的可用性。ACID追求一致性,而BASE追求可用性。
这可能也是BASE命名的用意,因为ACID作为一个单词时有”酸“的含义,而BASE作为一个单词时有”碱“的含义。
从BASE到CAP
CAP猜想
2000 年,Brewer近一步分析比较了 ACID 和 BASE,引入”网络分区“,并提出CAP 猜想:
在分布式系统中,最多能同时满足以下 3 个属性中的 2 个:
C (Consistency), A (Availability), P (Tolerance to network Partitions)
网络分区(Network Partitions)
WIKI给出的定义:网络分区指由于网络设备的failure,造成网络分裂为多个独立的组
在分布式系统中,不同的节点之间通过网络互相连通。但是由于网络故障,可能会出现网络的”分裂”现象,网络分成了不同的区域,区域内部之间可以的节点可以互相通信,而区域之间无法互相通信,这就是分区。
显然,网络分区是在分布式系统中常见的问题。
CAP定理
2002 年,GilBert 和 Lynch,重新定义了 CAP 这 3 个属性:
C(Consistency):一致性
在完成写请求后,不管客户端访问哪个节点,读取到的都是同一份最新写入的数据,这要求数据在不同节点上的副本保持一致。
A(Availability):可用性
任何来自客户端的请求,不管访问哪个非故障节点,都能得到响应数据,强调服务可用,但是不保证返回的数据是正确的。
P(Partition tolerance):分区容错性
Gilbert和Lynch在论文中定义的分区容错性:网络允许丢失一个节点发给另一个节点的任意多的消息
我觉得从CAP猜想中更容易理解什么是分区容错性:
Brewer提出的分区容错性是:Tolerance to network Partitions,直译过来就是”对网络分区的容忍”。
那么分区容错性含义就是:系统是否允许存在网络分区问题。
如果一个系统要求在运行过程中不能发生网络分区,那么这个系统就不具备分区容错性。
可以想象,一个单机的MySQL数据库不会发生网络分区,它肯定具有分区容错性。
在明确了上面关于可用性,一致性和分区容错性的定义之后,GilBert 和 Lynch证明了:
在分布式系统中, CAP 这 3 个属性不能同时达到,最多只能同时满足其中两个,这就是著名的CAP 定理。
需要注意的是CAP定理的前提是分布式系统,在单机应用中,不会发生网络分区,所以单机系统可以不具备分区容错性,那么此时CA可以同时达到,而对一个单机系统讨论分区容错性其实是毫无意义的。比如一个单机的数据库,就可以兼顾可用性和一致性,并不存在网络分区问题。
AP还是CP?
按照CAP定理的说法,我们只有三种选择:CA,AP和CP
那为什么说“由于分布式系统必须保证分区容错性,所以只能选择AP架构或者CP架构”?
答案是:”分布式系统中,网络分区无法避免”
前面提到,在分布式系统中,不同的节点之间通过网络互相连通,网络分区问题根本无法避免,所以分布式系统一定要容忍发生网络分区,也就是一定要具有分区容错性(Partition tolerance),所以分布式系统不能选择CA。(你无法要求在永远不发生网络分区的环境下运行分布式系统)
但是,具有分区容错性(Partition tolerance)并不意味着对于每一个客户端的请求,都只能选择A或者C中一个。当系统中的节点可以互相正常通信时,就可以同时保证可用性(Availability)和一致性(Consistency)。
那这是不是意味着分布式系统也能同时达到CAP呢?
答案是否定的。
网络分区不会一直存在,但也无法避免。一旦发生网络分区,分布式系统就只能在A和C之间做出选择:
- 保证一致性(Consistency),等待网络恢复时数据同步完成,才向客户端提供服务,等待同步的过程服务不可用。
- 保证可用性(Availability),总是响应客户端的请求,而不等待数据同步,服务可用但是不保证返回的数据是最新的。
可以看到,分布式系统在发生网络分区时,只能在A和C之间选择其一。换句话说,分布式系统不能永远同时保证可用性和一致性。所以,在设计分布式系统的时候,就要考虑清楚,如果发生了网络分区,要在A和C之间如何取舍,这决定了一个系统是AP原则还是CP原则:
- CP原则:如分布式数据库、分布式锁,服务注册中心Zookeeper
- AP原则:如DNS,服务注册中心Eureka
对比Eureka和Zookeeper在CAP中的选择
CAP定理可以给我们一些启示:
在设计分布式系统时,如果对数据正确性的要求很高,那么选择CP原则,牺牲可用性,保证一致性。如果对数据的一致性延迟不敏感,则可以选择AP原则,实现高可用。
下面是一些常见分布式系统对AP和CP选择:
名称 | CAP |
---|---|
Eureka | AP |
Consul | CP |
Zookeeper | CP |
Nacos | AP和CP两种模式都支持 |
Mongodb | CP |
那么为什么说,Eureka是AP原则,而Zookeeper是CP原则?
Eureka的各个节点是平等的,每个节点都可以提供查询和注册服务。
在发生网络分区故障时,Eureka一开始会从注册列表移除因长时间没收到心跳的服务,但是当EurekaServer节点在短时间内丢失过多客户端时,这个节点就会进入自我保护模式。此时Eureka不再从注册列表移除因长时间没收到心跳而应该过期的服务,并且仍然能够接受新服务的注册和查询请求,但是不会被同步到其他节点,这使得Eureka只要有一个节点存活就可以提供服务。
ZooKeeper采用Master/Slave 模式(主备模式),节点分为Leader,Observer和Follower角色。Follower 和 Observer 都只能提供读服务,只有Leader可以提供写服务,Zookeeper集群的写入服务遵循”写过半原则”
在发生网络分区故障时,某些和Leader不在一个分区的节点无法和Leader取得联系,此时Zookeeper节点不会提供服务,而是进去Leader选举流程,ZooKeeper在选举期间注册服务瘫痪,虽然服务最终会恢复,但是选举期间不可用的。在选举过程中会遵循“过半原则”,这可以保证数据只能通过集群中唯一的Leader写入,从而保证数据的一致性。
ACID,BASE和CAP的联系
回过头来看,CAP定理其实是对ACID和BASE两种思想的抽象总结:
CP原则延续了ACID的思想,发生网络分区时会等待网络恢复时数据同步完成,才向客户端提供服务,牺牲了系统的可用性。
AP原则延续了BASE的思想,总是响应客户端的请求,而不等待数据同步,牺牲数据的一致性。
那么,AP系统完全放弃了数据一致性吗?CP系统不具有可用性吗?
从前面讨论中已经可以得到答案,只是在发生网络分区的时候,AP优先放弃数据一致性,而CP优先放弃系统可用性。在网络恢复正常之后,AP系统最终会恢复数据一致性,CP系统最终会恢复可用性。
CP和ACID中一致性的区别
ACID的一致性是内部一致性,要求事务开始前和结束后,数据库的完整性约束没有被破坏。数据库的完整性约束包括:
- 实体完整性
- 参照完整性
- 用户定义的完整性
这里主要是为了防止参照完整性和用户定义的完整性被破坏,即约束、触发器、级联回滚等。
CAP的一致性是外部一致性,要求在完成写请求后,不管客户端访问哪个节点,读取到的都是同一份最新写入的数据,这要求数据在不同节点上的副本保持一致
AP和BASE中可用性的区别
AP和BASE看起来都选择可用性而牺牲了一致性,那么两者有什么区别呢?
从发展历史来看,BASE最早是Brewer在设计分布式系统时的经验总结,而AP是后来提出的CAP定理的推论
从可用性的角度来看,AP是高可用,而BASE是基本可用,原因在于,AP系统对数据一致性的要求更低,而对可用性的要求更高,AP系统如Eureka甚至只要有一个节点存活就能提供服务。而BASE允许在高负载或者系统故障的情况下损失一定的可用性,比如在请求过多时,可以选择非核心功能进行服务降级或熔断,保证核心功能可用。
BASE是谁提出的?
如果在百度或者谷歌上面搜索BASE理论,很容易得到一个结果就是:”BASE理论是由eBay架构师Dan Pritchett提出的,是对CAP中一致性和可用性权衡的结果,其来源于对大规模互联网系统分布式实践的总结, 是基于CAP定理逐步演化而来的”,但是我没有找到有可靠的资料表明这是事实,百度百科和维基百科上面都没有记载BASE理论的出处。
直到我找到这篇传说中Dan Pritchett介绍BASE理论的文章:Base: An Acid Alternative
从文章内容来看,Dan Pritchett并没有提出BASE理论,只是在介绍了CAP定理之后,又介绍了和ACID相对的BASE思想,而且也提到了
Following Brewer’s conjecture, if BASE allows for availability in a partitioned database, then opportunities to relax consistency have to be identified.
因此,Dan Pritchett的文章更像是在介绍Brewer的BASE思想,而不是提出BASE理论。
本文对于BASE出处的介绍,来自于NoSQL 数据库不应该放弃 Consistency这篇文章,个人认为比”BASE理论是由eBay架构师Dan Pritchett提出的”的可信度更高。
总结
在了解了ACID,BASE和CAP定理提出的背景之后,站在提出者的角度思考,更容易理解这些理论的真正含义。至此,文章开头提出的几个问题也有了答案:
- 分区容错性是在发生网络分区时系统仍然可以提供服务
- 分布式系统无法避免网络分区问题,所以必须保证分区容错性
- AP和BASE都是在需要保证分区容错性,又希望达到高可用时做出的妥协,并没有完全放弃一致性,只是允许出现暂时的数据不一致
- 可以放弃强一致性,允许”软状态“,最后达到最终一致性,实现提高可用性的目的
此外,如果不存在网络分区问题,也就不需要考虑分区容错性,此时A(Availability),C(Consistency)就可以同时实现。
参考资料
- 维基百科:ACID
- 维基百科:CAP定理
- CAP定理与BASE理论
- 数据完整性约束
- NoSQL 数据库不应该放弃 Consistency
- ACID 和 CAP 一致性的区别
- Base: An Acid Alternative
- 分布式-CAP理论和实现
- A clean way to implement database transaction in Golang