什么是分布式事务?

如果概念都了解,请移步
分布式事务的几种模式实现

分布式事务时指会涉及多个数据库(服务)的事务,其实就是对同一数据库事务的概念扩大到对多个数据库(服务)的事务。

目的是为了保证分布式系统中的数据一致性。

分布式事务处理的关键是必须有一种方法可以知道事务在任何地方所做的所有动作,提交或回滚事务的决定产生统一的结果(全部提交或全部回滚)。

分布式事务背景

数据库事务需要满足 ACID(原子性、一致性、隔离性、持久性)四个特性。

  • 原子性(Atomicity)指事务作为整体来执行,要么全部执行,要么全不执行。
  • 一致性(Consistency)指事务应确保数据从一个一致的状态转变为另一个一致的状态。
  • 隔离性(Isolation)指多个事务并发执行时,一个事务的执行不应影响其他事务的执行。
  • 持久性(Durability)指已提交的事务修改数据会被持久保存。

在单一数据节点中,事务仅限于对单一数据库资源的访问控制,称之为本地事务。几乎所有的成熟的关系型数据库都提供了对本地事务的原生支持。 但是在基于微服务的分布式应用环境下,越来越多的应用场景要求对多个服务的访问及其相对应的多个数据库资源能纳入到同一个事务当中,分布式事务应运而生

关系型数据库虽然对本地事务提供了完美的 ACID 原生支持。 但在分布式的场景下,它却成为系统性能的桎梏。如何让数据库在分布式场景下满足 ACID 的特性或找寻相应的替代方案,是分布式事务的重点工作。

本地事务

在不开启任何分布式事务管理器的前提下,让每个数据节点各自管理自己的事务。 它们之间没有协调以及通信的能力,也并不互相知晓其他数据节点事务的成功与否。 本地事务在性能方面无任何损耗,但在强一致性以及最终一致性方面则力不从心。

两阶段提交

XA协议最早的分布式事务模型是由 X/Open 国际联盟提出的 X/Open Distributed Transaction Processing (DTP) 模型,简称 XA 协议。

基于XA协议实现的分布式事务对业务侵入很小。 它最大的优势就是对使用方透明,用户可以像使用本地事务一样使用基于XA协议的分布式事务。 XA协议能够严格保障事务 ACID 特性。

严格保障事务 ACID 特性是一把双刃剑。 事务执行在过程中需要将所需资源全部锁定,它更加适用于执行时间确定的短事务。 对于长事务来说,整个事务进行期间对数据的独占,将导致对热点数据依赖的业务系统并发性能衰退明显。 因此,在高并发的性能至上场景中,基于XA协议的分布式事务并不是最佳选择。

两阶段事务提交采用的是 X/OPEN 组织所定义的DTP模型所抽象的 AP(应用程序), TM(事务管理器)RM(资源管理器) 概念来保证分布式事务的强一致性。 其中 TM 与 RM 间采用 XA 的协议进行双向通信。 与传统的本地事务相比,XA 事务增加了准备阶段,数据库除了被动接受提交指令外,还可以反向通知调用方事务是否可以被提交。 TM 可以收集所有分支事务的准备结果,并于最后进行原子提交,以保证事务的强一致性。

TC (Transaction Coordinator) - 事务协调者
维护全局和分支事务的状态,驱动全局事务提交或回滚。

TM (Transaction Manager) - 事务管理器
定义全局事务的范围:开始全局事务、提交或回滚全局事务。

RM (Resource Manager) - 资源管理器
管理分支事务处理的资源,与TC交谈以注册分支事务和报告分支事务的状态,并驱动分支事务提交或回滚。

XA规范的实现:

内部XA: 单机情况下,binlog充当TM的角色,一个事务过来,写入redolog日志,和undolog日志,事务提交时,同时写入redolog和binlog,保证redo log和binglog一致,如果事务撤销,则通过undo log进行撤销。

外部XA:分布式集群的情况下,一般加代理层来充当TM的角色,实现对事务的支持。

设计一个秒杀系统之分布式事务_java

两阶段提交协议2PC

以投票操作举例

  • 第一阶段

TM作为事务协调者,AP执行投票准备,RM执行prepare。

设计一个秒杀系统之分布式事务_分布式_02

  • 第二阶段

当第一阶段的反馈结果都为成功后,执行第二阶段,此时AP执行投票,RM执行commit。

设计一个秒杀系统之分布式事务_java_03

只要有一个AP(服务)准备阶段失败,执行回滚操作。

  • 2PC方案的缺点
  • 单点故障:协调者一旦发生故障,参与者与协调者失去同步,参与者会被一直阻塞,尤其是在提交阶段,所有参与者都处于锁定资源状态中,无法完成事务操作。
  • 阻塞:事务执行过程中所有参与者都是阻塞的,占用资源一直锁定,不会被释放,第三方参与者访问参与者占有的资源时会被阻塞,影响性能。
  • 数据不一致: 提交阶段协调者向参与者发送提交指令,发生局部网络故障,会出现一部分参与者未收到提交指令无法提交事务的情况,导致多个参与者之间出现数据不一致的现象(一般通过人工方式解决不一致)。

三阶段提交协议3PC

3PC是对2PC做了两点补充;

1、 引入超时机制。同时在协调者和参与者中都引入超时机制。

2、在第一阶段和第二阶段中插入一个准备阶段。保证了在最后提交阶段之前各参与节点的状态是一致的。

检查阶段(Can-commit)

设计一个秒杀系统之分布式事务_java_04

准备阶段(Pre-commit)

设计一个秒杀系统之分布式事务_分布式事务_05

提交阶段(Do-commit)

设计一个秒杀系统之分布式事务_协调者_06

  1. 只要有一个AP准备阶段失败,执行rollback回滚
  2. 如果在准备段等待超时,则自动提交
  • 优点
    预检机制: 通过预先检查系统运行状态,一定程度上减少了故障恢复的复杂性。
    超时机制: 如果在检查阶段等待超时,则自动终止,如果在准备阶段等待超时,则自动提交。
  • 缺点
    阻塞: 事务执行过程中占用的数据库资源依然被锁定,影响性能
    数据一致性: 如果在准备阶段,协调者和其中一个参与者都发生了故障,其他参与者会自动commit,那么就产生了数据不一致,这一点2PC同样存在(一般通过人工方式解决不一致)。

TCC

TCC事务其实主要包含两个阶段:Try阶段、Confirm/Cancel阶段。

  • 一阶段 prepare 行为
  • 二阶段 commit 或 rollback 行为

设计一个秒杀系统之分布式事务_java_07

一个电商支付场景来了解一下TCC的过程。

设计一个秒杀系统之分布式事务_分布式_08

上面是一个电商支付的场景步骤。

在TCC中的阶段如下:

Try阶段:

设计一个秒杀系统之分布式事务_分布式事务_09

Try成功,执行Confirm阶段:

设计一个秒杀系统之分布式事务_协调者_10

Try失败,执行Cancel阶段:

设计一个秒杀系统之分布式事务_java_11

  • 核心思想
  • 先执行try,如果成功,说明整个分布式系统运行状况良好。Confirm的执行成功概率很大。
  • 即使Try成功,Confirm依然有可能失败,例如:
    服务挂了,数据库,es等故障,资源不足,库存不够。
    但以为Try保存了中间状态,因此可以通过重试策略Confirm成功。
  • 优点
    避免了2PC和3PC的阻塞问题
    基于业务逻辑自定义事务粒度,而不是XA协议的纯数据库事务
    基于Confirm和Cancel的幂等性,保证数据的最终一致性
  • 缺点
    业务侵入很强,开发和维护成功很高,一般用在对分布式要求很高的金融场景

Saga

在Saga模式中,业务流程中每个参与者都提交本地事务,当出现某一个参与者失败则补偿前面已经成功的参与者,一阶段正向服务和二阶段补偿服务都由业务开发实现。

设计一个秒杀系统之分布式事务_协调者_12

  • 适用场景:

业务流程长、业务流程多
参与者包含其它公司或遗留系统服务,无法提供 TCC 模式要求的三个接口

  • 优势:

一阶段提交本地事务,无锁,高性能
事件驱动架构,参与者可异步执行,高吞吐
补偿服务易于实现

  • 缺点:

不保证隔离性