分布式事务概述

288 阅读4分钟

XA 规范

主要有三部分组成,应用程序AP,资源管理器RM,事务管理器TM。如下图所示。

XA规范属于两阶段递交。在第一阶段,应用程序向事务管理器发送事务请求,而事务管理器会依次向每个资源管理器发送预事务请求。各个资源管理器在接收到请求后,会依次在本地打开事务,并执行相应的操作。但此时事务是没有递交的。它们会向事务管理器返回已就绪(Ready)或未就绪(Not Ready)状态。

等所有的资源管理器都返回相应的状态后,则进入到了第二阶段。在这个阶段中,只有当第一阶段中所有的资源管理器节点都返回了Ready状态,才会递交事务,否则回滚事务。

这种两阶段递交的方式存在一些缺点;

事务的时间变长。

数据仍然会存在不一致的可能性。如在第二阶段的Commit阶段,如果发生了网络异常等情况。

三阶段的事务递交能够解决数据不一致的问题的可能性。

3PC 把 2PC 的准备阶段分为了准备阶段预处理阶段,在第一阶段只是询问各个资源节点是否可以执行事务,而在第二阶段,所有的节点反馈可以执行事务,才开始执行事务操作,最后在第三阶段执行提交或回滚操作。并且在事务管理器和资源管理器中都引入了超时机制,如果在第三阶段,资源节点一直无法收到来自资源管理器的提交或回滚请求,它就会在超时之后,继续提交事务。所以 3PC 可以通过超时机制,避免管理器挂掉所造成的长时间阻塞问题,但其实这样还是无法解决在最后提交全局事务时,由于网络故障无法通知到一些节点的问题,特别是回滚通知,这样会导致事务等待超时从而默认提交。

TCC事务

TCC事务主要由三个部分组成:Try Confirm,Cancel。 Try 阶段:主要尝试执行业务,执行各个服务中的 Try 方法,主要包括预留操作;

Confirm 阶段:确认 Try 中的各个方法执行成功,然后通过 TM 调用各个服务的 Confirm 方法,这个阶段是提交阶段;

Cancel 阶段:当在 Try 阶段发现其中一个 Try 方法失败,例如预留资源失败、代码异常等,则会触发 TM调用各个服务的 Cancel方法,对全局事务进行回滚,取消执行业务。

以上执行只是保证 Try 阶段执行时成功或失败的提交和回滚操作,你肯定会想到,如果在 Confirm 和 Cancel 阶段出现异常情况,那 TCC 该如何处理呢?此时 TCC 会不停地重试调用失败的 Confirm 或 Cancel 方法,直到成功为止。

但 TCC 补偿性事务也有比较明显的缺点,那就是对业务的侵入性非常大。首先,我们需要在业务设计的时候考虑预留资源;然后,我们需要编写大量业务性代码,例如 Try、Confirm、Cancel 方法;最后,我们还需要为每个方法考虑幂等性。这种事务的实现和维护成本非常高,但综合来看,这种实现是目前大家最常用的分布式事务解决方案。

业务无侵入方案——Seata(Fescar)

  • TM 向 TC 申请开启一个全局事务,全局事务创建成功并生成一个全局唯一的 XID;
  • XID 在微服务调用链路的上下文中传播;
  • RM 向 TC 注册分支事务,将其纳入 XID 对应全局事务的管辖;
  • TM 向 TC 发起针对 XID 的全局提交或回滚决议;
  • TC 调度 XID 下管辖的全部分支事务完成提交或回滚请求。

Seata 与其它分布式最大的区别在于,它在第一提交阶段就已经将各个事务操作 commit 了。Seata 认为在一个正常的业务下,各个服务提交事务的大概率是成功的,这种事务提交操作可以节约两个阶段持有锁的时间,从而提高整体的执行效率。那如果在第一阶段就已经提交了事务,那我们还谈何回滚呢?Seata 将 RM 提升到了服务层,通过 JDBC 数据源代理解析 SQL,把业务数据在更新前后的数据镜像组织成回滚日志,利用本地事务的 ACID 特性,将业务数据的更新和回滚日志的写入在同一个本地事务中提交。

如果 TC 决议要全局回滚,会通知 RM 进行回滚操作,通过 XID 找到对应的回滚日志记录,通过回滚记录生成反向更新 SQL,进行更新回滚操作。