目录


文章目录




分布式事务难题

随着分布式计算的发展,事务在分布式计算领域也得到了广泛的应用。

在单机数据库中,我们很容易能够实现一套满足 ACID 特性的事务处理系统,但在分布式数据库中,数据分散在各台不同的机器上,如何对这些数据进行分布式的事务处理具有非常大的挑战。


  • 原子性(Atomicity)要么全有,要么全无​:指事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生。
  • 一致性(Consistency)每次读取的都必须是最新数据​:指一个事务执行之前和执行之后数据库都必须处于一致性状态。如果事务成功地完成,那么系统中所有变化将正确地应用,系统处于有效状态。如果在事务中出现错误,那么系统中的所有变化将自动地回滚,系统返回到原始状态,即:从一个一致性状态变换到另外一个一致性状态。
  • 隔离性(Isolation)多并发事务互相隔离,对共享数据加锁​:指在并发环境中,当不同的事务同时操纵相同的数据时,每个事务都有各自的完整数据空间。由并发事务所做的修改必须与任何其他并发事务所做的修改隔离。事务查看数据更新时,数据所处的状态要么是另一事务修改它之前的状态,要么是另一事务修改它之后的状态,事务不会查看到中间状态的数据。
  • 持久性(Durability)数据最终的落盘变更是持久而不变的​:指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来即使数据库发生故障也不应该对其有任何影响。

分布式事务是指事务的参与者、支持事务的服务器、资源服务器以及事务管理器分别位于分布式系统的不同节点上,通常一个分布式事务中会涉及对多个数据源或业务系统的操作。

可以设想一个最典型的分布式事务场景:一个跨银行的转账操作涉及调用两个异地的银行服务,其中一个是本地银行提供的取款服务,另一个则是目标银行提供的存款服务,这两个服务本身是无状态并且相互独立的,共同构成了一个完整的分布式事务。如果从本地银行取款成功,但是因为某种原因存款服务失败了,那么就必须回滚到取款之前的状态,否则用户可能会发现自己的钱不翼而飞了。

从这个例子可以看到,一个分布式事务可以看做是多个分布式的操作序列组成的,例如上面例子的取款服务和存款服务,通常可以把这一系列分布式的操作序列称为子事务。因此,分布式事务也可以被定义为一种嵌套型的事务,同时也就具有了 ACID 事务特性。但由于在分布式事务中,各个子事务的执行是分布式的,因此要实现一种能够保证 ACID 特性的分布式事务处理系统就显得格外复杂。

分布式事务的实现方式

2PC

在事务的参与过程中会产生多个角色,暂时我们先这么理解,协调者负责事务的发起,而参与者负责执行事务。假定存在下面的 3 个角色,分别是一个协调和两个参与,此时我们需要 A、B 执行一个事务,并且要求这个事务,要么同时成功,要么同时失败。

2PC 也叫做二阶段提交:


  1. 一步执行
  2. 一步提交

架构师之路 — 分布式系统 — 分布式事务难题_数据库

2PC 阶段一,执行事务

此时协调者会先发出一个命令,要求参与者 A、B 都去执行这个事务,但是不提交。

说的再详细一点,就会产生写 redo,undo 的日志,锁定资源,执行事务。但是执行完了之后,直接向协调者打报告,询问一下,大哥我能提交吗?

这个在日常写 Java 的过程中应该经常遇到,就是前面写了一大堆操作,但是等到最后一定会写一个 conn.commit() 这样的东西,这就是所谓的执行但不提交。

2PC 阶段二:提交事务

当协调者收到第一阶段中的所有事务参与者(图中的 A,B)的反馈(这个反馈简单理解为,告诉协调者前面的第一阶段执行成功了)时,就发送命令让所有参与者提交事务。

说的再细一点,那就是协调者收到反馈,且所有参与者均响应可以提交,则通知参与者进行 commit,否则 rollback。

架构师之路 — 分布式系统 — 分布式事务难题_java_02

2PC 的 4 个缺点


  1. 性能​:整个流程看下来就知道这明显产生了同步阻塞,各个需要操作数据库的节点都占用了数据库的资源。只有当协调者收到所有节点都准备完毕的反馈,事务协调者才会通知 commit or rollback,而参与者执行完这个 commit or rollback 的操作后,才会去释放资源。
  2. 单点故障​:协调者才是这个事务的核心。假如此时协调者故障宕机,会导致通知无法传达到参与者的问题,比如收不到那个 commit or rollback,整一个事务便会停滞。
  3. 数据不一致​:协调者在第二阶段会发送 commit or rollback。可是这并不能保证每一个节点都正常收到这个命令,所以会可能窜在,参与者 A 收到了命令,提交了事务,但是参与者 B 没有。所以网络波动是永恒的病因,你永远无法躲开这个因素。
  4. 不存在容错机制​:这个协调者需要收到所有的节点反馈准备完成才会下达 commit 的指示,任意一个参与者的响应没有收到,协调者就会进行等待,而且只要存在一个宕机的节点,都会使得整个事务失败回滚。

3PC

3PC 在 2PC 的前提下进行了一个改良,将 2PC 中的准备阶段进行拆分,形成 3 个阶段:


  1. can commit
  2. pre commit
  3. do commit

并且引入超时机制,一旦事务参与者在指定时间内没有收到协调者的 commit or rollback 指令,就会自动进行本地 commit,解决协调者的单点故障问题。

3PC 第一阶段 can commit

协调者先进行询问,参与者就根据自身的实际情况回答 yes or no。

3PC 第二阶段 pre commit

如果参与者都是返回同意,协调者则向所有参与者发送预提交请求,并进入准备阶段,这里的准备阶段其实就是让参与者锁定资源,等待指令,然后就是事务的执行,此时也像 2PC 一样,执行但不提交。

3PC 第三阶段 do commit

然后等待协调者的指令,此时如果迟迟等不到指令,一段时间后就会自行本地提交。

3PC 的个缺点

但是这样也会存在弊端,比如协调者成功给 1,2 参与者都发送回滚,然后 3 刚好就没收到,那么 3 就自动提交了,所以超时机制其实并不能完全保证数据的一致性。