有分布式系统 CAP 加州大学伯克利分校的Eric理论 Brewer教授提出,在分布式系统中不可能同时满足一致性(Consistency)、可用性(Availability),以及分区容错(Partition Tolerance)。
- 一致性:分布式系统中的数据通常有多个副本,一致性描述了这些副本中数据在内容和组织上的一致性。
- 可用性:描述系统对用户的服务能力。所谓可用性,是指在用户能够容忍的时间范围内返回用户期望的结果。
- 分区容错性:分布式系统通常由多个节点组成。由于网络不可靠,分布式集群中的节点由于网络通信故障而被孤立成小集群,即网络分区。分区容错性要求系统在网络分区出现时仍能提供一致的可用服务。
对于分布式系统,我们应该始终假设网络不可靠,所以分区容错是分布式系统最基本的要求,我们的切入点是试图在可用性和一致性之间找到平衡,但这并不要求我们在系统设计中建立在网络分区的前提下,然后选择一致性和可用性。
Eric Brewer 教授在 2012 2000年,CAP被指出 在实际的系统设计指导中,理论证明不能同时满足一致性、可用性和分区容错性的观点存在误导性。传统对于 CAP 理论理解认为,在设计分布式系统时必须满足要求 P,然后在 C 和 A 两者之间的选择是片面的。事实上,网络分区的可能性相对较小,特别是目前的网络环境越来越好,甚至许多系统都有专线支持,所以当网络没有分区时,或者应该考虑 A 和 C。此外,还应对一致性、可用性和分区容错性有一个评估范围。最简单的是,就可用性而言,当有多少比例要求出现响应加班时,可以认为不符合可用性,而不是一旦加班就认为不可用。最后,我们需要考虑的是,分布式系统通常是一个大而复杂的系统。我们应该从较小的粒度来评估和设计每个子系统,而不是简单地从整体上做出武断的决定。
使分布式集群始终为外界提供可用的一致性服务,一直是一项具有挑战性和趣味性的任务。暂时抛开可用性,以一致性为例。对于关系数据库,我们通常使用事务来确保数据的强一致性。然而,当我们的数据量越来越大,无法承受单个库时,我们必须采取分库和表的策略来实现数据库的水平分割或引入 NoSQL 建立分布式数据库集群,以分担读写压力,从而提高数据库的存储和响应能力,然而,多个数据库的例子也给我们使用数据库带来了许多限制,如主键的整体唯一性、联表查询、数据聚合等。另一个棘手的问题是,数据库的事务已经从原来的单个数据库事务转变为当前的分布式事务。
分布式事务的实现并非没有解决办法,比如提交以下两个阶段(2PC):Two-Phase Commit)三阶段提交(3PCC):Three-Phase Commit)所有这些都为我们提供了思路,但在分布式环境下,如何保证数据的强一致性,并为外界提供高可用性的服务还是相当困难的,因此,许多分布式系统都远离数据的强一致性。
协议在两个阶段提交(2PCC):Two-Phase Commit)两阶段提交协议的目标是确保分布式系统的数据一致性,许多分布式系统使用该协议来支持分布式事务。顾名思义,该协议将分布式事务流程分为投票和事务提交两个阶段。为使整个数据库集群正常运行,该协议指定了协调员单点,用于协调整个数据库集群各节点的运行。为了简化描述,我们称数据库集群中的每个节点为参与者,协议的定义也包括协调者和参与者。
第一阶段:投票本阶段的主要目的是探索数据库集群中的参与者是否能够正常执行事务。具体步骤如下:
- 协调员将事务执行请求发送给所有参与者,并等待参与者反馈事务执行结果;
- 事务参与者收到请求后,执行事务但不提交,并记录事务日志;
- 参与者将自己的事务执行情况反馈给协调员,并阻止等待协调员的后续指令。
协调员询问第一阶段后,每个参与者都会回复自己事务的执行情况,此时存在 3 种可能性:
- 所有参与者都回复能够正常执行事务。
- 一个或多个参与者未能回复执行事务。
- 协调员等待加班。
对于第 1 在这种情况下,协调员将向所有参与者发出通知,具体步骤如下:
- 协调员向所有参与者发送协调员 commit 通知,要求提交事务;
- 参与者收到事务提交通知后执行 commit 操作,然后释放占有的资源;
- 参与者将事务返回协调员 commit 结果信息。
对于第 2 和第 3 在这种情况下,协调员认为参与者无法成功执行事务。为了整个集群数据的一致性,应将事务回滚通知发送给所有参与者。具体步骤如下:
- 协调员将事务发送给每个参与者 rollback 通知,要求回滚事务;
- 参与者收到事务回滚通知后执行 rollback 操作,然后释放占有的资源;
- 参与者将事务返回协调员 rollback 结果信息。
___两阶段提交协议解决了分布式数据库数据强一致性的问题_________________________________________
从协调者的角度来看,投票发起后进入 WAIT 状态,等待所有参与者回复各自的事务执行状态,并在收到所有参与者的回复后做出下一步的决定 commit 或 rollback 信息。从参与者的角度来看,在回复协调者的投票请求后进入 READY 状态(事务可以正常执行),下一步是等待协调员的最终决策通知。一旦收到通知,可以根据决策执行 commit 或 rollback 操作。
提交协议的原则简单易行,但缺点也很明显,包括以下几点:
- 单点问题
协调员在整个两个阶段的提交过程中起着重要的作用。一旦协调员的服务器停机,将影响整个数据库集群的正常运行。例如,在第二阶段,如果协调员因故障无法正常发送事务提交或回滚通知,参与者将始终处于堵塞状态,整个数据库集群将无法提供服务。
- 同步阻塞
在两个阶段的提交和实施过程中,所有参与者都需要听从协调员的统一调度,在此期间处于阻塞状态,不能从事其他操作,因此效率极低。
- 数据不一致
虽然这两个阶段的提交协议是由分布式数据的强一致性设计的,但仍然存在数据不一致性的可能性。例如,在第二阶段,假设协调员发布了事务 commit 但由于网络问题,该通知仅由部分参与者收到并执行 操作中,其他参与者因未收到通知而一直处于阻塞状态,导致数据不一致。
在很大程度上,可以引入超时机制和互查机制来解决上述问题。
如果协调员在指定时间内没有收到所有参与者的回应,则可以自动退出 WAIT 状态并发送给所有参与者 rollback 通知。如果参与者位于参与者的位置 READY 状态,但未在规定时间内收到协调员的第二阶段通知,不能武断执行 rollback 操作,因为协调员可以发送 commit 此时执行通知 rollback 会导致数据不一致。
此时,我们可以介入互询机制,让参与者参与 A 询问其他参与者 B 执行情况。如果 B 执行了 rollback 或 commit 操作,则 A 可以大胆的和 B 如果执行相同的操作; B 还没到 READY 状态,可以推断出协调者发出的肯定是 rollback 通知;如果 B 同样位于 READY 状态,则 A 您可以继续询问其他参与者。只有当所有的参与者都在位时 READY 状态时,协议不能在两个阶段提交处理时,会陷入长期堵塞状态。
三阶段提交协议(3PC):Three-Phase Commit)针对两阶段提交中存在的问题,三阶段提交协议通过引入预查询阶段和加班策略,减少整个集群的堵塞时间,提高系统性能。三个阶段分别提交:预查询(can_commit)、预提交(pre_commit),以及事务提交(do_commit)。
第一阶段:预询在这个阶段,协调员将询问每个参与者是否能够正常执行事务。参与者根据自己的情况回复估值。与实际执行事务相比,此过程较轻。具体步骤如下:
- 协调员将事务询问通知发送给各参与者,询问是否可以执行事务操作,并等待回复;
- 每个参与者根据自己的情况回复估值。如果估计他们可以正常执行事务,他们将返回确定信息并进入准备状态,否则他们将返回否定信息。
本阶段协调员将根据第一阶段的查询结果进行相应操作,查询结果主要包括 3 种:
- 所有参与者都返回确定信息。
- 一个或多个参与者返回否定信息。
- 协调员等待加班。
针对第 1 在这种情况下,协调员将事务执行请求发送给所有参与者,具体步骤如下:
- 协调员将事务执行通知发送给所有事务参与者;
- 参与者收到通知后执行事务,但未提交;
- 参与者将事务执行情况返回客户端。
在上述步骤中,如果参与者等待加班,事务将中断。对于第一个 2 和第 3 在这种情况下,协调员认为事务不能正常执行,所以他把它发给了所有的参与者 abort 通知,要求退出预备状态,具体步骤如下:
- 协调员将所有事务参与者发送给所有事务参与者 abort 通知;
- 收到通知后,参与者中断事务。
如果事务在第二阶段没有中断,协调员将根据事务返回的结果决定提交或回滚事务,分为 3 种情况:
- 所有参与者都能正常执行事务。
- 一个或多个参与者未能执行事务。
- 协调员等待加班。
针对第 1 在这种情况下,协调员向所有参与者提出请求,具体步骤如下:
- 协调员将事务发送给所有参与者 commit 通知;
- 所有参与者在收到通知后执行 commit 操作,释放占有的资源;
- 参与者将事务提交结果反馈给协调员。
针对第 2 和第 3 在这种情况下,协调员认为事务不能成功执行,因此向所有参与者发送回滚请求,具体步骤如下:
- 协调员将事务发送给所有参与者 rollback 通知;
- 所有参与者在收到通知后执行 rollback 操作,释放占有的资源;
- 参与者将事务回滚结果反馈给协调员。
在这个阶段,如果参与者因为协调者或网络问题而无法收到协调者, commit 或 rollback 如果要求,参与者将不会像两个阶段提交时那样陷入障碍,而是等待加班后继续 commit,与两阶段提交相比,虽然同步阻塞减少,但数据的不一致性仍然无法完全避免。
两阶段提交协议中长期阻塞状态的概率仍然很低。因此,虽然三阶段提交协议比两阶段提交协议更能保证数据的强一致性,但由于效率问题,两阶段提交协议在实际系统中更受欢迎。