从 ACID 和 BASE 到 CAP
事务的 ACID 特性:原子性(Atomicity),一致性(Consistency),隔离性(Isolation),持续性(Durability)。
分布式系统无法严格地满足 ACID 特性,如果满足严格的 ACID 必然会导致分布式系统变得无法使用。所以可用性和一致性是一个无法两全其美的方案。
基于此,学者提出了 BASE 和 CAP 的分布式系统的经典理论。
ACID —— 追求一致性
在数据库系统中,为了保证数据的完整性,总结出了事务的4个特性:原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)和持久性(Durability)。
- 原子性:在一组操作中,要么全部操作执行成功,要么全部操作执行失败,不存在中间数据,也不存在部分操作执行成功或失败的情况。在事务执行的过程中,如果某一个操作失败了,那么整个事务操作都将回滚(Rollback),恢复至事务开始的状态。
- 一致性:事务的所有操作不会破坏数据的完整性,一个系统在数据完整的状态下执行事务之后,数据仍然是完整的。这里的完整性包含以下两个方面:
- 规则完整性,包含数据类型和精度。
- 数据完整性,操作执行串联性,符合预期结果。
- 隔离性:在多个事务在执行期间,事务与事务之间互不影响。
- 持久性:在事务执行成功之后,所有操作的执行结果都是永久的,哪怕服务发生故障。
隔离级别
(Isolation Level) 控制一个事务将修改的数据暴露给在其他并发事务的程度。更弱的数据库隔离级别以暴露更多事务未提交的更改为代价,提高整体并发度,但这种并发度可能造成一系列问题:
- Dirty Reads (脏读):读取未提交的数据
- Unrepeatable Reads (不可重复读):多次读取同一对象的结果不同 (侧重点在数据库的读取与更新)
- Phantom Reads (幻读):由于插入或删除操作,导致多次相同范围扫描查询的结果不同 (侧重点在数据库的插入与删除)
BASE 理论 —— 追求可用性
BASE 是 Eric Brewer 在演讲中提出了一种弱于 ACID 特性的架构模型,为了系统的健壮性而依赖于软状态,是对一致性和可用性权衡后所得的结果。为此,EricBrewer 团队为这个架构模型选择了一个新词,即 BASE。这个词明显是刻意为之,ACID 有酸性的意思,BASE 有碱性的意思,二者对立。
BASE理论的核心思想是:在某些场景中,为保证系统的可用性无须做到强一致性,同时每个应用可以采用适当的方式使系统数据达到最终的一致性。
BASE 理论主要包含基本可用(Basically Available)、软状态(Soft State)和最终的一致性(Eventually Consistent)三个方面。
- 基本可用:当分布式系统出现故障的时候,允许损失部分可用性,但不等于系统不可用。
这里的损失部分可用性通常包括两个方面:响应时间的损失和在功能上降级。- 响应时间的损失:当部分节点宕机或者机房出现故障时,在请求增加响应时间的基础上需要给客户端返回正确的结果,而不是拒绝服务。
- 在功能上降级:当处于流量高峰时,一部分请求可能会返回降级的数据,而不会真正请求后端核心系统,以保证后端系统的稳定性。
- 软状态:指允许系统中的数据存在中间状态,并认为该状态不会影响系统的整体可用性,即允许节点之间的数据同步存在延迟。
🆚 ACID 中的原子性,我们认为,在 ACID 事务中,所有操作要么全部成功,要么全部失败。当 ACID 事务结束后不会残留事务的中间状态,而 BASE 理论中的软状态则是指允许出现事务的中间状态。例如,节点之间的投票协商和多副本之间的数据同步都需要进行网络交互,交互过程中的状态即是软状态的一种体现。 - 最终的一致性:系统的所有副本在经过一个时间期限后最终达到一致的状态。
在BASE理论中,系统允许出现事务的中间状态(软状态),但是在经过一个时间期限后,要求事务结束,所有操作要么全部成功,要么全部失败。
对于不同的业务场景和架构设计,时间期限长短有较大的差异,只要整体在用户能够接受的范围内即可。每个应用可以根据自身的业务特点、采用适当的方式使系统最终达到一致性。
CAP 理论
CAP 定理是 Brewer 在 2000 年的 Towards Robust Distributed Systems 演讲中提出的猜想。在演讲中,Brewer 对 ACID 和 BASE 做了进一步对比分析,在 ACID 的一致性(Consistency,简写为 C)和 BASE 的可用性(Availability,简写为 A)的基础上扩展出了一个新的维度,即分区容错性(Partition tolerance,简写为 P),以此组成 CAP 猜想。【BASE 满足的是 AP 方案】
2002年,Lynch 在发表的 Brewer’s Conjecture and the Feasibility of Consistent, Available, Parition-Tolerant WebServices 论文中证明了猜想,从此 CAP 猜想上升为定理。
CAP定理 (CAP theorem),又被称作布鲁尔定理 (Brewer’s theorem),它指出对于一个分布式计算系统来说,不可能同时满足以下三点:
- 一致性 (Consistency):等同于所有节点访问同一份最新的数据副本;
- 可用性 (Availability):每次请求都能获取到成功与否的响应,即使由于分区导致消息丢失——但是不保证获取的数据为最新数据;
- 分区容错性 (Partition tolerance):以实际效果而言,分区相当于对通信的时限要求。系统如果不能在时限内达成数据一致性,就意味着发生了分区的情况,必须就当前操作在 C 和 A 之间做出选择;
【简单解释:分区容错性是,尽管任意数量的消息因节点间的网络问题而丢失或者延迟,系统仍能正确运行】
CAP 理论的理解
- 一致性:系统中的所有数据副本在同一时刻都保持一致,且是最新的。
- 可用性:系统始终能够对外提供服务。
- 分区容错性:系统在发生网络分区的情况下仍然能够正常运行。
❗️CAP 中的一致性 C 和数据库事务 ACID 特性中的一致性 C 不同。
ACID 中 C 指的是事务的一致性状态,即:数据库任何数据库事务修改数据必须满足定义好的规则,包括数据完整性 (约束)、级联回滚、触发器等。事务执行前后,保证数据的完整性(系统从一个正确的状态迁移到另一个正确的状态)。
CAP 中的 C 可理解为数据副本的一致性,即:集群中的所有副本的值都是一样的。
为什么 CAP 三者无法共存
在没有网络分区和网络波动的情况下,我们无需为了 P 而舍弃 C 或者 A;但是一个大集群中的网络几乎无时不刻都存在问题 (网络分区或者网络波动时刻发生),因此:为了保证 P (让整个分布式系统还是可用的),就要舍弃 C 和 A 中的一个。
🌰 在不考虑 Quorum 复制机制 (Quorum Replication) 和共识算法的情况下:假设存在 3 个副本,则对于这 3 个副本的写入有 2 种可能的方案:
- (一写):向 3 个副本写入,只要 1 个副本返回写入成功即认为成功;
- (三写):向 3 个副本写入,必须 3 个副本都写入成功才认为成功;
一写的情况下,由于只要有一个副本写入成功即可返回成功;当存在网络分区后 (如图 S1 的网络和其他节点中断),三台机器的数据就可能出现不一致的情况,因此无法保证 C;但由于可以正常的返回写入成功,所以 A 可以保证。
三写的情况下,要三个副本都写入成功才可返回成功,出现网络分区的故障后,由于无法保证三个节点都写入成功,因此最终会返回报错,所以没有保证 A;但由于数据没有被成功写入,因此保证了 C。
总结:在分布式系统中,无法避免 P(因为网络常常是不可靠的)。在数据一致性和系统可用性中,开发者如果选择数据一致性,例如当系统发生写入操作时,在数据没有确认写入所有副本之前,系统就需要面对可能不可用的现实;当开发者强调系统可用性时,系统可以一直接受写操作的请求,但是极端条件下,读操作有可能无法返回最新写入的数据。
辩证看待 CAP 理论
CAP 理论是一个证明在现实场景下你无法达到什么的理论(即:追求CAP都保证),这个理论限制了分布式系统设计者的行为,类似于能量守恒理论限制了你无法设计出永动机。
其 C 和 A 的条件都太过极端,就导致有些理解不深入的设计者认为选取了一个,就一定抛弃了另一个。实际设计一个系统时,是要根据实际场景,在保证一个指标的同时,对另一个指标进行 降级
处理,尽量的去保证另一指标。也就是一个 tradeoff 的过程。
数据一致性分类
Werner Vogels: Eventually Consistent. ACM Queue 6(6): 14-19 (2008)
作者从客户端和服务端角度来叙述数据一致性的概念。
客户端 (Client):用户是如何观察到数据更新的。
服务端 (Server):数据更新在系统中流通方式,以及系统能给这些更新的一致性保证。
客户端角度,数据一致性可分为:
总的来说,就强一致性 (线性一致性),弱一致性两种,其他的一致性都是弱一致性的特殊情况。
强一致性 ⭐️
又称为 线性一致性、原子一致性。
当一个更新完成后,后续所有的访问都能看到更新之后的值。(就像一个单机系统)
弱一致性 ⭐️
系统不保证后续的访问能返回更新的值,在返回新值前需要保证一定的条件,这短时间叫 不一致窗口
。这种一致性在实际应用中提到的不多,我的理解是弱一致性系统需要给出一个确定的不一致窗口的值。
顺序一致性
系统不保证后续的访问能返回更新的值,但不同节点对数据的更新必须在所有节点上以相同的顺序看到。
最终一致性
很多一致性协议都属于最终一致性。当没有更新操作后,存储系统保证最终会将新值返回给所有访问者。当不考虑失败时,不一致窗口可以通过一些度量值计算出来。
因果一致性
如果进程 A 通知进程 B 它已更新了一个数据项,那么进程 B 的后续访问将返回更新后的值,且一次写入将保证取代前一次写入。与进程 A 无因果关系的进程 C 的访问遵守一般的最终一致性规则。
因果一致性往往发生在分区(也称为分片)的分布式数据库中,
分区后,每个节点并不包含全部数据。不同的节点独立运行,因此不存在全局写入顺序。如果用户A提交一个问题,用户B提交了回答。问题写入了节点A,回答写入了节点B。因为同步延迟,发起查询的用户可能会先看到回答,再看到问题。
为了防止这种异常,需要另一种类型的保证:因果一致性。 即如果一系列写入按某个逻辑顺序发生,那么任何人读取这些写入时,会看见它们以正确的逻辑顺序出现。
一种实现方案是应用保证将问题和对应的回答写入相同的分区。但并不是所有的数据都能如此轻易地判断因果依赖关系。如果有兴趣可以搜索向量时钟深入此问题。
读己之所写
总能读到自己写进去的新值。因为自己写了相当于通知了自己,是因果一致性的特例。
实现方式:
- (最简单方案)对于某些特定的内容,都从主库读。举个例子,知乎个人主页信息只能由用户本人编辑,而不能由其他人编辑。因此,永远从主库读取用户自己的个人主页,从从库读取其他用户的个人主页。
- 如果应用中的大部分内容都可能被用户编辑,那这种方法就没用了。在这种情况下可以使用其他标准来决定是否从主库读取,例如可以记录每个用户最后一次写入主库的时间,一分钟内都从主库读,同时监控从库的最后同步时间,任何超过一分钟没有更新的从库不响应查询。
- (更好的方案)客户端可以在本地记住最近一次写入的时间戳,发起请求时带着此时间戳。从库提供任何查询服务前,需确保该时间戳前的变更都已经同步到了本从库中。如果当前从库不够新,则可以从另一个从库读,或者等待从库追赶上来。
会话一致性
是因果一致性模型的实用版本,它把访问存储系统的进程放到会话的上下文中。只要会话还存在,系统就保证“读己之所写”一致性。如果由于某些失败情形令会话终止,就要建立新的会话,而且系统的保证不会延续到新的会话。
单调读一致
如果进程已经看到过数据对象的某个值,那么任何后续访问都不会返回在那个值之前的值。
单调读比强一致性更弱,比最终一致性更强。
实现方式:确保每个用户总是从同一个节点进行读取(不同的用户可以从不同的节点读取),比如可以基于用户ID的哈希值来选择节点,而不是随机选择节点。
单调写一致
系统保证来自同一个进程的写操作顺序执行。要是系统不能保证这种程度的一致性,就非常难以编程了。
K-possibility一致
保证读出来的数据有一定的概率是最近写的 k 个版本中的一个。
T-possibility一致
保证在写操作提交后经过 t 秒后读出旧数据的概率不超过一定值。
服务器角度:
这个角度主要观察副本和读写策略。如何尽快将更新后的数据分布到整个系统,降低达到最终一致性的时间窗口,是提高系统的可用度和用户体验非常重要的方面。定义:
- :副本总数
- :写副本数(当有 个副本写成功,就视为写操作成功)
- :读副本数(当读到了 个副本,就视为读操作成功)
一致性度量:
-
:强一致。抽屉原理,总能读到一个新的副本。
-
:弱一致。例如对于一主一备异步复制的关系型数据库,,则如果读的是备库,就可能无法读取主库已经更新过的数据,所以是弱一致性。当写成功的节点少于 ,则写失败。
更多例子:
- 如果 ,任何一个写节点失效,都会导致写失败,因此可用性会降低,但是由于数据分布的 个节点是同步写入的,因此可以保证强一致性,而且优化了读场景,随便读一个副本就可以。
- 那些关心容错而不关心一致性的系统,设置 。
- 如果 ,只需要一个节点写入成功即可,是优化了写入效率,写性能比较高。但是在节点失效时不能保证持久性。
- 如果 ,并且写入的节点不重叠的话,则会存在写冲突。
最终一致性:DNS,Gossip (Cassandra 的通信协议)
线性一致性:分布式一致性协议 2PC、3PC,共识算法 paxos、Raft (Multi-Paxos)、ZAB (Multi-Paxos)
用 CAP 理论分析分布式数据库
前面提到,在分布式的环境下,P 一定存在。因此一旦出现了网络分区,一致性和可用性就一定要抛弃一个。
- 对于 NoSQL 数据库,由于更加注重可用性(例如:缓存等场景),因此会是一个 AP 系统;
- 对于分布式关系型数据库,必须要保证一致性,因此就是一个 CP 系统;
❗️虽然分布式关系型数据库是一个 CP 系统,但是仍然是具有高可用性需求的,虽然达不到理论上 100% 的可用性,但是一般都具备5个9(99.999%)以上的高可用(如通过主备切换,重新选主等,还是能恢复正常服务的)。
所以可将分布式关系型数据库看作是 CP + HA (Hign Availability) 的系统,也因此产生了两个重要且广泛使用的指标:
- RPO (Recovery Point Objective,恢复点目标):指数据库在灾难发生后会丢失多长时间的数据。分布式关系型数据库 RPO = 0;
- RTO (Recovery Time Objective,恢复点时间):指数据库在灾难发生后到整个系统恢复正常所需要的时间。分布式关系型数据库 RTO < 几分钟。
可以认为:
- RPO 对应了 CAP 中的 C,因此一般情况下 RPO 都是 0,因为数据一致性是保证的,没有副本丢失数据;
- RTO 对应 HA,虽然不能完全保证 A,但是可以达到高可用;
用 CAP 视角看目前成熟的分布式方案
前面提到了 Quorum 复制机制 (Quorum Replication) 和共识算法,这里来简单看一下。
Quorum Replication
Quorum Replication 主要包括三个部分:
- :副本数;
- :写入成功副本数;
- :读取成功副本数;
当 时,在系统中永远都存在至少一个副本是最新且正确的。 因此,对于系统中的多个节点,只需要保证 (大多数成功即成功)。
当 时有两种设计:
- (写可用 AP,读一致 CP):🌰 记录流水系统,流水是源源不断产生并需持续写入的,因此不能容忍写入的阻塞,所以写入追求可用性;读取流水可入容许暂时失败,但不能容忍读取的流水是不正确的,因此读要保证一致性。
- (写一致 CP,读可用 AP):🌰 电子商务系统,商家修改商品金额,可以容许暂时失败或阻塞,但是会有大量客户去查看商品,需要保证读可用。
Consensus
在共识算法中,各个副本之间存在交互,整个集群中会选举出一个 leader (使用 raft、paxos 算法等),由 leader 提供读写操作。
- 当 leader 没有出现故障的情况下,整个集群的 CA 都可以得到保障。
- 当 leader 由于部分原因宕机,集群选举新的 leader,此时系统会在选举的短暂过程中不可用;
因此,共识算法通常都是一个 CP + HA 的系统。
这里共识算法的 CP + HA 中 C 是指:对于外界的观察者而言,共识系统返回的数据是一致的。但是系统内部的所有成员在某一时刻的数据并不都是一致的。
共识算法可理解为放弃一部分成员的一致性而增加了可用性。
参考链接
宝藏 UP 戌米的论文笔记
本文作者:Joey-Wang
本文链接:https://www.cnblogs.com/joey-wang/p/17624151.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步