浅谈分布式事务

通俗的理解,事务是一组原子操作单元。我们希望一些列的操作能够全部正确执行,如果这一组操作中的任意一个步骤发生错误,那么就需要回滚之前已经完成的操作。也就是同一个事务中的所有操作,要么全都正确执行,要么全都不要执行。

传统的单机应用系统一般使用一个关系型数据库,利用数据库事务来保证数据的一致性,下面先了解一下本地数据库事务的一些特性。

一、本地数据库事务

事务从数据库角度说,就是一组 SQL 指令,要么全部执行成功,若因为某个原因其中一条指令执行有错误,则撤销先前执行过的所有指令。

关系型数据库(例如:MySQL、SQL Server、Oracle 等)事务都有以下几个特性:原子性 (Atomicity)、一致性( Consistency )、隔离性或独立性( Isolation) 和持久性(Durabilily),简称就是 ACID。

  • 原子性:表示事务执行过程中的任何失败都将导致事务所做的任何修改失效。
  • 一致性:表示当事务执行失败时,所有被该事务影响的数据都应该恢复到事务执行前的状态。
  • 隔离性:表示在事务执行过程中对数据的修改,在事务提交之前对其他事务不可见。
  • 持久性:表示已提交的数据在事务执行失败时,数据的状态都应该正确。

数据库事务操作也比较简单:开始一个事务,改变(插入,删除,更新)很多行,然后提交事务(如果有异常时回滚事务)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
Connection con = null;
try {
// 工具类得到 connection 对象
con = JdbcUtils.getConnection();
// 关闭自动提交,开启事务
con.setAutoCommit(false);
// 增、删、改 等操作
...

// 成功操作后提交事务
con.commit();
con.close();
} catch (Exception e) {
try {
// 如果有异常时回滚事务
con.rollback();
con.close();
} catch (SQLException e1) {
e1.printStackTrace();
}
}

更进一步,借助开发平台中的数据访问技术和框架(如:Spring),我们需要做的事情更少,只需要关注数据本身的改变。

随着组织规模不断扩大,业务量不断增长,单机应用和数据库已经不足以支持庞大的业务量和数据量,这个时候需要对应用和数据库进行拆分,就出现了一个应用需要同时访问两个或两个以上的数据库情况。开始我们用分布式事务来保证一致性。

二、分布式事务理论

2.1 CAP 定理

CAP 定理是由加州大学伯克利分校 Eric Brewer 教授提出来的,他指出 WEB 服务无法同时满足一下 3 个属性:

  • 一致性 (Consistency) : 客户端知道一系列的操作都会同时发生 (生效)
  • 可用性 (Availability) : 每个操作都必须以可预期的响应结束
  • 分区容错性 (Partition tolerance) : 即使出现单个组件无法可用, 操作依然可以完成

CAP 理论告诉我们,在分布式系统中,C、A、P 三个条件中我们最多只能选择两个。那么问题来了,究竟选择哪两个条件较为合适呢?

对于一个业务系统来说,可用性和分区容错性是必须要满足的两个条件,并且这两者是相辅相成的。业务系统之所以使用分布式系统,主要原因有两个:

  • 提升整体性能:当业务量猛增,单个服务器已经无法满足我们的业务需求的时候,就需要使用分布式系统,使用多个节点提供相同的功能,从而整体上提升系统的性能,这就是使用分布式系统的第一个原因。
  • 实现分区容错性:单一节点或多个节点处于相同的网络环境下,那么会存在一定的风险,万一该机房断电、该地区发生自然灾害,那么业务系统就全面瘫痪了。为了防止这一问题,采用分布式系统,将多个子系统分布在不同的地域、不同的机房中,从而保证系统高可用性。

这说明分区容错性是分布式系统的根本,如果分区容错性不能满足,那使用分布式系统将失去意义。

此外,可用性对业务系统也尤为重要。在大谈用户体验的今天,如果业务系统时常出现 “系统异常”、响应时间过长等情况,这使得用户对系统的好感度大打折扣,在互联网行业竞争激烈的今天,相同领域的竞争者不甚枚举,系统的间歇性不可用会立马导致用户流向竞争对手。因此,我们只能通过牺牲一致性来换取系统的可用性(A)分区容错性(P)。这也就是下面要介绍的 BASE 理论。

2.2 BASE 理论

CAP 理论告诉我们一个悲惨但不得不接受的事实——我们只能在 C、A、P 中选择两个条件。而对于业务系统而言,我们往往选择牺牲一致性来换取系统的可用性和分区容错性。不过这里要指出的是,所谓的 “牺牲一致性” 并不是完全放弃数据一致性,而是牺牲强一致性换取弱一致性。下面来介绍下 BASE 理论。

  • Basically Available(基本可用)整个系统在某些不可抗力的情况下,仍然能够保证 “可用性”,即一定时间内仍然能够返回一个明确的结果。
  • Soft state(软状态)同一数据的不同副本的状态,可以不需要实时一致。
  • Eventually consistent(最终一致性)同一数据的不同副本的状态,可以不需要实时一致,但一定要保证经过一定时间后仍然是一致的。

BASE 理论是对 CAP 中的一致性和可用性进行一个权衡的结果,理论的核心思想就是:我们无法做到强一致,但每个应用都可以根据自身的业务特点,采用适当的方式来使系统达到最终一致性(Eventual consistency)。

有了以上理论之后,我们来看一下分布式事务的问题。

三、分布式事务

3.1 两阶段提交(2PC)

和上一节中提到的数据库 XA 事务一样,两阶段提交就是使用 XA 协议的原理,我们可以从下面这个图的流程来很容易的看出中间的一些比如 commit 和 abort 的细节。

两阶段提交这种解决方案属于牺牲了一部分可用性来换取的一致性。

优点: 尽量保证了数据的强一致,适合对数据强一致要求很高的关键领域。(其实也不能 100% 保证强一致)

缺点: 实现复杂,牺牲了可用性,对性能影响较大,不适合高并发高性能场景,如果分布式系统跨接口调用。

3.2 补偿事务(TCC)

TCC 其实就是采用的补偿机制,其核心思想是:针对每个操作,都要注册一个与其对应的确认和补偿(撤销)操作。它分为三个阶段:

  • Try 阶段主要是对业务系统做检测及资源预留
  • Confirm 阶段主要是对业务系统做确认提交,Try 阶段执行成功并开始执行 Confirm 阶段时,默认 Confirm 阶段是不会出错的。即:只要 Try 成功,Confirm 一定成功。
  • Cancel 阶段主要是在业务执行错误,需要回滚的状态下执行的业务取消,预留资源释放。

举个例子,假入 Bob 要向 Smith 转账,思路大概是:
我们有一个本地方法,里面依次调用
1、首先在 Try 阶段,要先调用远程接口把 Smith 和 Bob 的钱给冻结起来。
2、在 Confirm 阶段,执行远程调用的转账的操作,转账成功进行解冻。
3、如果第 2 步执行成功,那么转账成功,如果第二步执行失败,则调用远程冻结接口对应的解冻方法 (Cancel)。

优点: 跟 2PC 比起来,实现以及流程相对简单了一些,但数据的一致性比 2PC 也要差一些

缺点: 缺点还是比较明显的,在 2,3 步中都有可能失败。TCC 属于应用层的一种补偿方式,所以需要程序员在实现的时候多写很多补偿的代码,在一些场景中,一些业务流程可能用 TCC 不太好定义及处理。

欣赏此文?求鼓励,求支持!
显示 Disqus 评论
0%
Title - Artist
0:00