分布式事务概念及解决方案
微服务分布式架构中的分布式事务是一个技术难点,为了保证数据的一致的,必须要解决分布式事务问题。
分布式事务的两个基本理论是 CAP 和 BASE,为实现分布式解决方案提供了理论方向。实现分布式事务的解决方案主要有两种类型,一种是基于强一致性协议实现,另一种是柔性事务实现数据最终一致性。
分布式事务概念
ACID
ACID 是事务的四个特性。
- Atomicity:原子性,指所有在事务中的操作要么都成功,要么都不成功,所有的操作都不可分割,没有中间状态。一旦某一步执行失败,就会全部回滚到初始状态。
- Consistency:一致性,事务执行的结果,必须使数据库的状态从一种一致状态,变到另一种一致状态,一致性是通过原子性来保证的。。
- Isolation:隔离性,即不同事务之间的相互影响和隔离的程度。
- Durability:持久性,事务一旦提交,持久化到物理数据库中。
CAP
CAP 指在一个分布式系统中,Consistency(一致性)、 Availability(可用性)、Partition tolerance(分区容错性),三者不可得兼。
CAP 原则的精髓是要么 AP,要么 CP,不存在 CAP。一致性 和 可用性 不能同时成立,因为分布式环境下肯定会分区部署多个副本节点,这必然会带来一致性问题,即保证多个节点的数据是相同的;而要让多个节点数据相同,就必须花时间来同步数据,这还必须在通信不会出现失败的情况下,那么在同步数据过程中为了保持一致性,就不能对外提供服务,所以这段时间就无法满足可用性的问题。
总结一句话,因为网络无法 100% 可靠,分布式环境下必然分区,就必然存在一致性的问题。
- Consistency:一致性,在分布式系统分布式系统中的所有数据备份,在同一时刻是否同样的值。
- Availability:可用性,在集群中一部分节点故障后,集群整体是否还能响应客户端的读写请求。
- Partition tolerance:分区容错性,当某些节点或分区网络故障时,依然能保证服务可用。节点越多,备份越多,容错性越高。
BASE
Base 理论是对 CAP 中 一致性 和 可用性 权衡的结果,其来源于对大型互联网分布式实践的总结,是基于 CAP 定理逐步演化而来的。其核心思想是:既是无法做到强一致性(Strong consistency),但每个应用都可以根据自身的业务特点,采用适当的方式来使系统达到最终一致性(Eventual consistency)。
- Basically Available:基本可用,出现故障,但本还能用,例如可能增加响应延迟。
- Soft state:软状态,又称柔性状态,允许多个不同节点的数据副本存在数据延时,但不影响系统的整体可用。
- Eventually consistent:最终一致性,软状态必须是有期限的,在期限过后,应当保证所有副本数据一致性,从而达到最终一致性。
分布式事务场景
单一服务单一数据库可通过 ACID 来保证自身事务的处理。在分布式微服务环境下,不同的服务有各自独立的的数据库,或数据库本身分库分表,单一的事务处理是无法保证此环境下数据一致性问题,就需要分布式事务来解决。
两个服务 Foo Service 和 Bar Service,Foo Service 插入一件 Foo 对象到 Foo Table 中, 需要调用 Bar Service 服务也插入一条 Bar 对象到 Bar Table 中,这是一个典型的分布式调用场景。
当 Bar Service 插入 Bar 对象异常时,就需要回滚自己的事件,同时 Foo Service 也要回滚之前提交的事务,这两个事务分属不同的系统就是分布式的,还要确保是原子性的。
分布式事务实现类型
事务如果按保证数据一致性的方案来划分,可分为 强一致性事务 和 柔性事务。
强一致性事务
在单应用单数据库的本地事务,或后续提到的二阶段提交,是强一致性事务。
强一致性事务在分布式环境下会产生阻塞,对性能影响较大。
柔性事务
柔性事务是基于 BASE 理论实现的,即确保数据的最终一致性,如 TCC 补偿型事务,本地消息表(异步确认)属于柔性事务。
分布式事务解决方案
两阶段提交
概念
在事务处理、数据库和计算机网络中,两阶段提交协议(2PC)是一种原子提交协议(ACP)。它是一种分布式算法,协调参与分布式原子事务的所有进程,决定是否提交或中止(回滚)事务(这是一种特殊的共识协议)。
在分布式系统中,每个节点可以知道自己的操作是否成功,但无法知道其它节点的操作是否成功。当一个事务跨越多个节点时,为了保证事务的 ACID 特性,需要引入一个作为 协调者 的组件来收集所有节点的操作结果,并做出该事务是提交,还是撤消的最后决定;其它所有节点都是 参与者,负责管理本地事务的执行(提交 或 回滚)。
基本算法
- 提交请求(投票阶段):协调者向所有参与者发起提交或中止事务的投票请求,如果事务参与者的本地部分执行已正确结束,则回复
Yes
提交;若检则到本地部分出现问题,则回复No
中止。- 协调者向所有参与者发送一个提交消息的查询,并等待直到收到所有参与者的回复。
- 参与者执行本地事务直到被要求提交为止,如果参与者的执行成功,则回复一条协议消息(投票 Yes ,赞成提交),若失败,则回复一条中止消息(投票 No,不同意提交)。
- 执行阶段(提交阶段):当协调者都收到所有参与者都投票
Yes
时,就作出提交的决定,否则中止交易,并将结果通知给所有参与者,参与者使用本地事务资源来做出相应的动作(提交 或 回滚)。- 协调者收到所有参与者都投票赞成提交,就向所有参与者发送 提交 的消息,每个参与者收到消息就执行本地事务的提交,完成操作后,释放事务其间持有的所有锁和资源(回滚),并向协调者回复一个确认消息(ACK),协调者收到确认时,事务完成 。
- 任何参与者在提交请求阶段(或协调者的超时时间到期)投反对票,协调者向所有参与者发送回滚消息。每个参与者使用撤销日志撤消事务,并释放事务期间保留的资源和锁,并向协调者发送一个确认(ACK),当协调收到确认时,撤消事务。
消息流
1 | Coordinator(协调员) Participant(参与者) |
缺点
- 同步阻塞问题:两阶段提交协议的最缺点是它是一种阻塞协议。在执行过程中,所有参与节点都是事务阻塞型的,直到收到提交或回滚,这在分布式环境下是严重影响性能的。
- 单点故障:由于协调者的重要性,一旦协调者发生故障永久失败,则某些参与者将永远无法解决事务,整个分布式事务可能一直阻塞。
- 数据不一致:当协调者发送 commit 之后,某些节点因网络故章而没有收到请求而没有执行 commit 操作,则存在数据不一致问题。
三阶段提交
概念
三阶段提交协议(3PC)是一种分布式算法,它允许分布式系统中的所有节点都同意提交事务(个人理解:所有节点都参与是否同意提交事务的决定)。
但与两阶段提交协议(2PC)不同,3PC是非阻塞的。 具体来说,3PC在事务提交或中止之前所需的时间量设置了上限(即引入了超时机制)。 此属性确保如果给定事务尝试通过 3PC 提交并保留一些资源锁,它将在超时后释放这些锁。
动机
三阶段提交可以说是两阶段提交的优化或补充,在 2PC 的准备阶段和提交阶段之间,插入预提交阶段(preCommit),预提交阶段是个参与者的最终确认,保证了在最后提交阶段各参与者的状态是一致的。
在提交阶段,两阶段提交协议不能可靠地从 协调者 和 参与者 的失败中恢复。如果只有协调者失败,并且所有参与者没有收到提交消息,就可以安全地推断出没有发生任何提交。但是,如果协调者和参与者都失败了,则失败的参与者有可能是第一个被通知,并且实际上已经完成了提交。即使选择了新的协调者,在收到所有参与者的协议之前,它也不能自信地继续操作,因此必须阻止,直到所有队列成员作出响应。
三阶段提交协议通过引入预提交状态来消除这个问题。如果协调员在发送预调试消息之前失败,所有参与者将一致同意该操作已中止。在所有参与者确认(ACKed)他们 准备提交(Prepared Commit)之前,协调者不会发出doCommit
消息(达到状态一致性)。这消除了任何参与者在所有参与者接收到决定之前实际完成事务的可能性(在两阶段提交协议中需要无限阻塞的模糊性)。
协议描述
协调者
协调者接收事务请求。如果此时出现故障,则协调者中止事务。否则协调者发送一个
canCommit?
消息给参与者,并进入等待状态。如果出现故障、超时或协调者在等待状态下收到
No
消息,协调者将中止该事务,并向所有参与者发送中止消息。否则,协调者将在时间窗口内收到来自所有参与者回复的
Yes
消息,并向所有参与者发送 预提交消息(preCommit),并进入已准备状态。如果协调者成功处于准备状态,它将进入提交状态(doCommit)。如果协调者在等待来自参与者的确认时超时,则会中止该事务。如果协调者接收到大部分参与者回复的确认信息,也会转移到提交转态。
参与者
- 参与者收到来自协调者的
canCommit?
消息。如果参与者同意,就向协调者发送Yes
消息,并进入 准备状态;若不同意,则发送No
消息中止。如果发生故障,则进入中止状态。 - 在已准备的状态中,如果参与者从协调器接收到中止消息,失败或等待提交超时,则它将中止。 如果参与者收到
preCommit
消息,则回复ACK
消息并等待最终提交或中止。 - 如果在参与者收到预提交消息后,协调器失败或超时,则参与者继续提交。
- 参与者收到来自协调者的
缺点
- 主要缺点是,如果网络连接断开,它就无法恢复。
- 协议至少需要三次往返才能完成,至少需要三次往返时间(RTT)。这可能是完成每个事务的长延迟。
TCC补偿型
TCC(Try-Confirm-Cancel)也是比较常见的分布式事务解决方案。TCC 分别对应 Try,Confirm,Cancel 三种操作,含义如下:
- Try:完成所有业务检查(一致性),预留业务资源(准隔离性)。
- Confirm:确认执行业务操作,不再做任何业务检查,只使用 Try 阶段预留的业务资源。
- Cancel:取消执行业务,释放 Try 阶段预留的业务资源。
在跨服务的业务操作中,首先通过 Try 锁住服务中的业务资源进行资源预留,只有资源预留成功了,才能继承后续的操作。Confirm 操作是在 Try 之后进行的操作,对 Try 阶段锁定的资源进行业务操作。Cancel 则是在操作失败时用于回滚。
TCC 的操作与业务结合紧密,耦合性较强。
开源的 TCC 框架:
- https://github.com/changmingxie/tcc-transaction
- https://github.com/prontera/spring-cloud-rest-tcc
- https://github.com/QNJR-GROUP/EasyTransaction
- https://github.com/liuyangming/ByteTCC
最终一致性
分布式事务的本质是解决数据一致性,实现方案主要有两种:
- 一种是基于 JavaEE 的分布式事务规范(
JTA
)实现的分布式事务技术框架(Spring JOTM、Atomikos),保证数据的强一致性
,但实现比较复杂且对系统性能有影响; - 第二种是基于本地事件表 和 MQ 结合 实现数据的
最终一致性
,可以轻松解决分布式事务问题。(本地事件表又称为事件溯源(Event-Sourcing),通过异步确保数据一致性,个人认为 事件溯源 是更准确的描述)。