一、序言
本文介绍分布式中刚性事务常见的几种方案。有关分布式事务的基础概念可参考博主的上篇文章《分布式事务(一)》。
二、XA 协议
XA 协议是由 X/Open 组织提出的分布式事务处理规范。它主要定义了事务管理器(Transaction Manager,简称 TM)和资源管理器(Resource Manager,简称 RM )之间的接口。
在分布式系统中,XA 协议主要用于保证在多个不同的系统或资源中进行的事务能够协同工作,从而保证数据的一致性。这种协议在处理分布式事务时非常重要。
目前,主流的数据库,如 Oracle、DB2、MySQL 等,都实现了 XA 接口,都可以作为 RM。
XA 协议主要包括两个阶段:
- 准备阶段(Prepare):事务协调者向所有的事务参与者发送准备请求。事务参与者收到请求后,如果可以提交事务,回复 “Yes”,否则回复 “No”。
- 提交阶段(Commit):如果所有事务参与者都回复了 “Yes”,事务协调者向所有事务参与者发送提交请求,否则发送回滚请求。
三、2PC
3.1 准备阶段
- 首先,事务协调者向所有事务参与者发送准备信号
- 事务参与者接收到准备请求之后向事务协调者回复一个就绪的应答信号
- 事务协调者正常接收到所有事务参与者的就绪信号之后,准备阶段完成
3.2 提交/回滚阶段
3.2.1 提交阶段
- 当准备阶段完成之后,事务协调者向所有事务参与者发送事务提交信号
- 事务参与者接收到提交信号之后,开始提交各自的事务。事务提交完成之后,事务参与者向事务协调者回复一个已提交的应答信号
- 事务协调者正常接收到所有事务参与者的已提交应答信号之后,提交阶段完成
3.2.2 回滚阶段
如果事务协调者未接收到正常的应答信号,就会进入回滚阶段。
- 事务协调者向所有事务参与者发起事务回滚信号
- 事务参与者接收到回滚信号之后,开始回滚事务。事务回滚完成之后,事务参与者向事务协调者回复一个已回滚的应答信号
- 事务协调者接收到所有事务参与者的已回滚应答信号之后,回滚阶段完成
3.3 2PC 的问题
-
准备阶段异常
发送准备信号前 发送准备信号后 协调者异常 事务未开始,无影响 参与者收到准备信号,锁定资源。但是由于参与者无法接收到协调者的下一次信号会导致资源一直被锁定 参与者异常 事务未开始,无影响 若没有锁定资源,资源便不会被占用 -
提交/回滚阶段异常
发送提交/回滚信号前 发送提交/回滚信号后 协调者异常 参与者无法正常接收协调者后续的命令,前期锁定的资源无法被释放 若参与者执行正常,资源将会被正常释放 参与者异常 参与者无法正常执行协调者后续的命令,前期锁定的资源无法被释放 参与者无法正常执行提交/回滚操作,前期锁定的资源无法被释放
四、3PC
2PC 有一个严重的问题是:由于协调者异常或参与者异常可能会导致资源被长期占用无法释放。3PC 为了解决上述 2PC 的问题,一共新增了两项内容:
- 新增一个预提交阶段
- 参与者引入超时机制
4.1 预提交阶段
3PC 的三个阶段分别是:
- CanCommit(准备阶段):事务协调者向所有参与者发送 CanCommit 请求,询问它们是否可以提交事务。如果所有参与者都准备好提交,则进入下一个阶段;如果有参与者无法提交,则中止事务(此阶段资源没有被锁定)。
- PreCommit(预提交阶段):事务协调者向所有参与者发送 PreCommit 请求,通知它们即将提交事务。参与者在收到 PreCommit 请求后,会执行事务的预提交操作,此时会锁定资源(资源被锁定,但事务没有被提交)。
- DoCommit(执行提交阶段):如果所有参与者成功执行了预提交操作,事务协调者向所有参与者发送 DoCommit 请求,要求它们执行事务的最终提交操作。在收到 DoCommit 请求后,参与者会正式提交事务并释放事务资源。
3PC 将 2PC 的准备阶段拆分并扩展成了 CanCommit、PreCommit 。之所以这样做,是因为 CanCommit 不会像 2PC 中的准备阶段一上来就把资源锁定。CanCommit 先询问所有参与者的状态是否正常(即在事务操作之前先保证所有参与者都是是正常的)。多了一个询问的过程,可以起到一定预防异常的作用(因为已经询问过了各参与者,而且各参与者都给出了应答,理论上可以推断出参与者几乎不会发生异常)。
4.2 超时机制
之前在讨论 2PC 问题的时候,协调者如果发生异常,参与者将无法正常接收到命令导致资源一直被占用。为了解决这一问题,3PC 引入了参与者超时机制:
- 若在 PreCommit 阶段协调者发生异常,参与者没有正常接收到命令,等待指定时间后会自动取消(因为 PreCommit 阶段参与者没有正常执行,资源并未锁定,所以不需要释放资源)
- 若在 DoCommit 阶段协调者发生异常,参与者没有正常接收到命令,等待指定时间后会自动执行提交任务、释放资源
4.3 3PC 的问题
3PC 是在 2PC 的基础上,多引入了一个阶段和超时机制。这是牺牲了一部分性能来换取系统的可靠性,但是这也是治标不治本的。因为 3PC 只是尽最大的可能去减少异常的发生,而不是避免了异常。同时,3PC 多了一个阶段这将会增加系统的复杂性。
五、FAQ
5.1 选择 2PC 方案还是 3PC 方案
选择使用 2PC 还是 3PC 取决于系统的具体需求和应用场景,主要考虑以下因素:
使用 2PC 的情况:
- 性能优先:如果系统对性能要求较高,且可以容忍一定程度的阻塞和一致性风险,可以选择 2PC。由于 2PC只涉及两个阶段和少量的消息交换,通常具有较低的性能开销。
- 简单应用场景:对于简单的分布式事务场景,没有特别高的一致性要求,并且系统中的节点之间的网络连接比较可靠,可以选择 2PC 来简化实现和降低复杂度。
- 易于部署和维护:2PC 相对于 3PC 来说,实现和部署更为简单,因此对于资源有限或者部署环境复杂的系统来说,可以考虑使用 2PC。
使用 3PC 的情况:
- 一致性要求高:如果系统对一致性要求较高,不能容忍数据不一致或者事务中断的情况,应该选择 3PC。由于 3PC 引入了明确的中间状态和额外的确认阶段,能够在某些情况下避免一致性问题。
- 复杂应用场景:对于复杂的分布式事务场景,可能涉及到多个参与者和协调者、网络分区、节点故障等复杂情况,此时选择 3PC 可以提高系统的可靠性和一致性。
- 可以接受一定性能开销:由于 3PC 引入了额外的确认阶段和消息交换,通常具有较高的性能开销,因此对于一致性要求较高、可以接受一定性能开销的场景来说,选择 3PC 是比较合适的。