分布式事务——2PC、3PC 和 TCC

发布时间:2020-06-23 12:54:46 作者:wx5d6cccb1cb158
来源:网络 阅读:2793

对于单机下的本地事务,很显然我们有已被实践证明的成熟 ACID 模型来保证数据的严格一致性。但对于一个高访问量、高并发的分布式系统来说,如果我们期望实现一套严格满足 ACID 特性的分布式事务,很可能出现的情况就是在系统的可用性和严格一致性之间出现冲突——因为当我们要求分布式系统具有严格一致性时,很可能就要牺牲掉系统的可用性。但毋庸置疑的一点是,可用性又是一个所有用户不允许我们讨价还价的属性,比如像淘宝这样的网站,我们要求它 7x24 小时不间断地对外服务。因此,我们需要在可用性和一致性之间做一些取舍,围绕这种取舍,出现了两个经典的分布式理论——CAP 和 BASE,这两者也是所有分布式事务协议的基石。

一、CAP 定理

CAP 首次在 ACM PODC 会议上作为猜想被提出,两年后被证明为定理,从此深深影响了分布式计算的发展。CAP 理论告诉我们,一个分布式系统不可能同时满足一致性(Consistency)、可用性(Availability)和分区容错性(Partition tolerance)这三个基本需求,最多只能同时满足其中的两项。

CAP 定理告诉了我们同时满足这三项是不可能的,那么放弃其中的一项会是什么样的呢?

放弃项

放弃P : 如果希望能够避免出现分区容错性问题,一种较为简单的做法是将所有数据放在一个节点上。这样肯定不会受网络分区影响。但此时分布式系统也失去了意义。因此在实际的架构设计中,P是一定要满足的。

放弃A: 放弃可用性就是在系统遇到网络分区或其他故障时,受影响的服务可以暂时不对外提供,等到系统恢复后再对外提供服务。

放弃C: 放弃一致性不代表完全放弃数据一致性,这样的话系统就没有意义了。而是放弃数据的强一致性,保留最终一致性。这样的系统无法保证数据保持实时的一致性,但能够承诺数据最终会达到一个一致的状态。

实际的实现中,我们往往会把精力花在如何根据业务特点在 C(一致性)和 A(可用性)之间寻求平衡。

二、BASE 理论

BASE 是 Basically Available(基本可用)、Soft state(软状态)和 Eventually consistent(最终一致性)三个短语的简写。BASE 是对 CAP 中一致性和可用性权衡的结果,其来源于对大规模互联网系统分布式实践的总结。其核心思想是:即使无法做到强一致性,但每个应用都可以根据自身的业务特点,采用适当的方式来使系统达到最终一致性。

严格遵守 ACID 的分布式事务我们称为刚性事务,而遵循 BASE 理论的事务我们称为柔性事务。在分布式环境下,刚性事务会让系统的可用性变得难以忍受,因此实际生产中使用的分布式事务都是柔性事务,其中使用最多的就是 2PC、3PC 和 TCC。

三、2PC 协议

2PC 是二阶段提交(Two-phase Commit)的缩写,顾名思义,这个协议分两阶段完成。第一个阶段是准备阶段,第二个阶段是提交阶段,准备阶段和提交阶段都是由事务管理器(协调者)发起的,协调的对象是资源管理器(参与者)。二阶段提交协议的概念来自 X/Open 组织提出的分布式事务的规范 XA 协议,协议主要定义了(全局)事务管理器和(局部)资源管理器之间的接口。XA 接口是双向的系统接口,在事务管理器以及一个或多个资源管理器之间形成通信桥梁。Java 平台上的事务规范 JTA(Java Transaction API)提供了对 XA 事务的支持,它要求所有需要被分布式事务管理的资源(由不同厂商实现)都必须实现规定接口(XAResource 中的 prepare、commit 和 rollback 等)。

两阶段如下:

两阶段提交协议成功场景示意图如下:


分布式事务——2PC、3PC 和 TCC



我们看到两阶段提交协议在准备阶段锁定资源,是一个重量级的操作,并能保证强一致性,但是实现起来复杂、成本较高,不够灵活,更重要的是它有如下致命的问题:

上面所有的这些问题,都是需要人工干预处理,没有自动化的解决方案,因此两阶段提交协议在正常情况下能保证系统的强一致性,但是在出现异常情况下,当前处理的操作处于错误状态,需要管理员人工干预解决,因此可用性不够好,这也符合 CAP 定理的一致性和可用性不能兼得的原理。

四、3PC 协议

三阶段提交协议(3PC 协议)是两阶段提交协议的改进版本。它通过超时机制解决了阻塞的问题,并且把两个阶段增加为三个阶段:

三阶段提交协议成功场景示意图如下:


分布式事务——2PC、3PC 和 TCC



这里与两阶段提交协议有两个主要的不同:

三阶段提交协议相比二阶段提交协议,避免了资源被无限锁定的情况。但也增加了系统的复杂度,增加了参与者和协调者之间的通信次数。

五、TCC 协议

无论是 2PC 还是 3PC,都存在一个大粒度资源锁定的问题。为了解释这个问题,我们先来想象这样一种场景,用户在电商网站购买商品1000元,使用余额支付800元,使用红包支付200元。我们看一下在 2PC 中的流程:

prepare 阶段:

commit 阶段:

为什么说这是一种大粒度的资源锁定呢?是因为在 prepare 阶段,当数据库给用户余额减 800 元之后,为了维持隔离性,会给该条记录加锁,在事务提交前,其它事务无法再访问该条记录。但实际上,我们只需要预留其中的 800 元,不需要锁定整个用户余额。这是 2PC 和 3PC 的局限,因为这两者是资源层的协议,无法提供更灵活的资源锁定操作。为了解决这个问题,TCC 应运而生。TCC 本质上也是一个二阶段提交协议,但和 JTA 中的二阶段协议不同的是,它是一个服务层的协议,因此开发者可以根据业务自由控制资源锁定的粒度。我们等会儿可以看到 TCC 在上面这个场景中的优势,但在那之前,我们先来看一下 TCC 协议的运行过程。

TCC 将事务的提交过程分为 try-confirm-cancel(实际上 TCC 就是 try、confirm、cancel 的简称) 三个阶段:

和 JTA 二阶段事务的参与方都要实现 prepare、commit、rollback 一样,TCC 的事务参与方也必须实现 try、confirm、cancel 三个接口。流程如下:

  1. 事务发起方向事务协调器发起事务请求,事务协调器调用所有事务参与者的 try 方法完成资源的预留,这时候并没有真正执行业务,而是为后面具体要执行的业务预留资源,这里完成了一阶段。

  2. 如果事务协调器发现有参与者的 try 方法预留资源时候发现资源不够,则调用参与方的 cancel 方法回滚预留的资源,需要注意 cancel 方法需要实现业务幂等,因为有可能调用失败(比如网络原因参与者接受到了请求,但是由于网络原因事务协调器没有接受到回执)会重试。

  3. 如果事务协调器发现所有参与者的 try 方法返回都 OK,则事务协调器调用所有参与者的 confirm 方法,不做资源检查,直接进行具体的业务操作。

  4. 如果协调器发现所有参与者的 confirm 方法都 OK 了,则分布式事务结束。

  5. 如果协调器发现有些参与者的 confirm 方法失败了,或者由于网络原因没有收到回执,则协调器会进行重试。这里如果重试一定次数后还是失败,会怎么样?常见的是做事务补偿。

TCC 执行场景示意图如下:

分布式事务——2PC、3PC 和 TCC


现在我们再回到开始的那个支付场景中,看看 TCC 在该场景中的流程:

Try操作

Confirm操作

Cancel操作

可以看到,我们使用了冻结代替了原先的账号锁定(实际操作中,冻结操作可以用数据库减操作+日志实现),这样在冻结操作之后,事务提交之前,其它事务也能使用账户余额,提高了并发性。

总结一下,相比于二阶段提交协议,TCC 主要有以下区别:


推荐阅读:
  1. 分布式事务详解
  2. 分布式事务面试题

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

java 程序员 分布式

上一篇:ECMAScript模块化和CommonJS模块化的区别是什么

下一篇:ASP.NET MVC Model绑定(六)

相关阅读

您好,登录后才能下订单哦!

密码登录
登录注册
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》