事务分类 - 图1

1 本地事务

不用事务的编程框架来管理事务,直接使用资源管理器来控制事务。典型的就是java.sql.Connection 中的 setAutoCommit、commit、rollback方法。
应用都只需要操作单一的数据库,这种情况下的事务称之为本地事务( Local Transaction )。本地事务的ACID特性是数据库直接提供支持。本地事务应用架构如下所示:
image.png

1.1 事务隔离级别

image.png
以MYSQL数据库来分析四种隔离级别

  • 第一种隔离级别:Read uncommitted(读未提交)

如果一个事务已经开始写数据,则另外一个事务不允许同时进行写操作,但允许其他事务读此行数据,该隔离级别可以通过“排他写锁”,但是不排斥读线程实现。这样就避免了更新丢失,却可能出现脏读,也就是说事务B读取到了事务A未提交的数据
解决了更新丢失,但还是可能会出现脏读

  • 第二种隔离级别:Read committed(读提交)

如果是一个读事务(线程),则允许其他事务读写,如果是写事务将会禁止其他事务访问该行数据,该隔离级别避免了脏读,但是可能出现不可重复读。事务A事先读取了数据,事务B紧接着更新了数据,并提交了事务,而事务A再次读取该数据时,数据已经发生了改变。
解决了更新丢失和脏读问题

  • 第三种隔离级别:Repeatable read(可重复读取)

可重复读取是指在一个事务内,多次读同一个数据,在这个事务还没结束时,其他事务不能访问该数据(包括了读写),这样就可以在同一个事务内两次读到的数据是一样的,因此称为是可重复读隔离级别,读取数据的事务将会禁止写事务(但允许读事务),写事务则禁止任何其他事务(包括了读写),这样避免了不可重复读和脏读,但是有时可能会出现幻读。(读取数据的事务)可以通过“共享读镜”和“排他写锁”实现。
解决了更新丢失、脏读、不可重复读、但是还会出现幻读

  • 第四种隔离级别:Serializable(可序化)

提供严格的事务隔离,它要求事务序列化执行,事务只能一个接着一个地执行,但不能并发执行,如果仅仅通过“行级锁”是无法实现序列化的,必须通过其他机制保证新插入的数据不会被执行查询操作的事务访问到。序列化是最高的事务隔离级别,同时代价也是最高的,性能很低,一般很少使用,在该级别下,事务顺序执行,不仅可以避免脏读、不可重复读,还避免了幻读
解决了更新丢失、脏读、不可重复读、幻读(虚读)

1.2 事务执行流程

例如:mysql>update test set wyt = ‘yutao’ where id=1;更新一行数据

  1. 事务开始
  2. 从磁盘中读取id=1这行数据到内存缓冲池(Buffer Pool)
  3. 申请锁资源,对这行数据上排他锁
  4. 将需要修改的数据页读取到缓冲池
  5. 记录修改前的这行数据到undo log(一个刷盘过程)
  6. 修改数据(数据直接写入磁盘,没有刷盘过程)
  7. 修改后的数据写入到 redo log buffer(一个刷盘过程)
  8. 第6步修改后的数据准备提交
  9. binlog二进制日志文件写入磁盘提交事务,成功,失败就回滚或者重做

image.png
image.png

1.3 本地事务优缺点

本地事务的优点

  • 支持严格的ACID属性
  • 可靠
  • 高效
  • 状态可以只在资源管理器中维护
  • 应用编程模型简单

本地事务的局限

  • 不具备分布式事务处理能力
  • 隔离的最小单位由资源管理器决定,如数据库中的一条记录

    2 全局事务

    此处全局事务被限定为一种适用于单个服务使用多个数据源场景的事务解决方案。
    资源管理器管理和协调的事务,可以跨越多个数据库和进程。资源管理器一般使用 XA 二阶段提交协议与“企业信息系统”(EIS) 或数据库进行交互。
    事务管理器控制着全局事务,管理事务生命周期,并协调资源。资源管理器负责控制和管理实际资源。
    image.png

    2.1 J2EE中全局事务的实现

    Java自身提供了一些API可以用来实现全局事务。Java中的事务——JDBC事务和JTA事务中介绍的JTA事务就可以用来实现J2EE中的全局事务。
    image.png

    2.2 全局事务的优缺点

    全局事务,作为一种标准的分布式事务解决方案,他解决了本地事务无法满足分布式场景中数据的ACID的要求。
    关于分布式事务、两阶段提交协议、三阶提交协议中我曾经介绍过,2PC本身是存在同步阻塞问题,这就会导致效率变低,所以,采用2PC进行事务控制的全局事务也必然存在效率低的问题。这也是全局事务最致命的缺点,在提倡微服务的今天,这是不能容忍的。

    3 共享事务

    共享事务(Share Transaction)是指多个服务共用同一个数据源。这里有必要再强调一次“数据源”与“数据库”的区别:数据源是指提供数据的逻辑设备,不必与物理设备一一对应。
    如果直接将不同数据源视为不同的数据库,那完全可以用全局事务或者下一讲要学习的分布式事务来实现。
    如果针对每个数据源连接的都是同一个物理数据库的特例,一种理论可行的方案是,直接让各个服务共享数据库连接。同一个应用进程中共享数据库连接并不困难,但不同服务节点共享数据库连接很难做到,为了实现共享事务,就必须新增一个中间角色。
    这在分布式的场景下是个伪需求,你有充足理由让多个微服务去共享数据库,那就必须找到更加站得住脚的理由,来向团队解释拆分微服务的目的是什么。

    4 分布式事务

    分布式事务(Distributed Transaction)特指多个服务同时访问多个数据源的事务处理机制。

    4.1 CAP与ACID

    分布式系统(distributed system)的最大难点,就是各个节点的状态如何同步。CAP 定理是这方面的基本定理。
    一致性(Consistency):代表在任何时刻、任何分布式节点中所看到的数据都是符合预期的。
    可用性(Availability):代表系统不间断地提供服务的能力。密切相关两个指标:可靠性(平均无故障时间:MTBF)和可维护性(平均可修复时间:MTTR)。
    分区容忍性(Partition Tolerance):代表分布式环境中部分节点因网络原因而彼此失联(即与其他节点形成“网络分区”)时,系统仍能正确地提供服务的能力。
    如果放弃分区容忍性(CA without P),意味着我们将假设节点之间通信永远是可靠的。永远可靠的通信在分布式系统中必定不成立的。主流的 RDBMS(关系数据库管理系统)集群通常就是采用放弃分区容错性的工作模式。
    如果放弃可用性(CP without A),意味着我们将假设一旦网络发生分区,节点之间的信息同步时间可以无限制地延长,此时,问题相当于退化到前面“全局事务”中讨论的一个系统使用多个数据源的场景之中,我们可以通过 2PC/3PC 等手段,同时获得分区容忍性和一致性。著名的 HBase 也是属于 CP 系统。
    如果放弃一致性(AP without C),意味着我们将假设一旦发生分区,节点之间所提供的数据可能不一致。选择放弃一致性的 AP 系统目前是设计分布式系统的主流选择,因为 P 是分布式网络的天然属性,而 A 通常是建设分布式的目的。目前大多数 NoSQL 库和支持分布式的缓存框架都是 AP 系统,如Redis集群。
    CAP、ACID 中讨论的一致性称为“强一致性”(Strong Consistency)。把牺牲了 C 的 AP 系统,叫做“最终一致性”(Eventual Consistency)。它是指,如果数据在一段时间内没有被另外的操作所更改,那它最终将会达到与强一致性过程相同的结果,有时候面向最终一致性的算法,也被称为“乐观复制算法”。

    4.2 TCC事务

    TCC 是另一种常见的分布式事务机制,它是“Try-Confirm-Cancel”三个单词的缩写。
    TCC 的实现过程分为了三个阶段:

  • Try:尝试执行阶段,完成所有业务可执行性的检查(保障一致性),并且预留好全部需用到的业务资源(保障隔离性)。

  • Confirm:确认执行阶段,不进行任何业务检查,直接使用 Try 阶段准备的资源来完成业务处理。Confirm 阶段可能会重复执行,因此本阶段所执行的操作需要具备幂等性。
  • Cancel:取消执行阶段,释放 Try 阶段预留的业务资源。Cancel 阶段可能会重复执行,也需要满足幂等性。
  • 它是一种业务侵入性较强的事务方案,要求业务处理过程必须拆分为“预留业务资源”和“确认 / 释放消费资源”两个子过程。

TCC 其实有点类似 2PC 的准备阶段和提交阶段,但 TCC 是位于用户代码层面,而不是在基础设施层面,这为它的实现带来了较高的灵活性,可以根据需要设计资源锁定的粒度。TCC 在业务执行时只操作预留资源,几乎不会涉及锁和资源的争用,具有很高的性能潜力。但是 TCC 并非纯粹只有好处,它也带来了更高的开发成本和业务侵入性,意味着有更高的开发成本和更换事务实现方案的替换成本。
通常我们并不会完全靠裸编码来实现 TCC,而是基于某些分布式事务中间件(譬如阿里开源的Seata)去完成,尽量减轻一些编码工作量。

4.3 SAGA事务

TCC 事务具有较强的隔离性,避免了“超售”的问题,而且其性能一般来说是本篇提及的几种柔性事务模式中最高的,但它仍不能满足所有的场景。TCC 的最主要限制是它的业务侵入性很强,不止是它需要开发编码配合所带来的工作量,而更多的是指它所要求的技术可控性上的约束。譬如,网银支付,通常也就无法完成冻结款项、解冻、扣减这样的操作。
SAGA 事务基于数据补偿代替回滚的解决思路。大致思路是把一个大事务分解为可以交错运行的一系列子事务集合。每个子事务都应该是或者能被视为是原子行为;为每一个子事务设计对应的补偿动作。
两种恢复策略:

  • 正向恢复(Forward Recovery):T1,T2,…,Ti(失败),Ti(重试)…,Ti+1,…,Tn
  • 反向恢复(Backward Recovery):T1,T2,…,Ti(失败),Ci(补偿),…,C2,C1。

与 TCC 相比,SAGA 不需要为资源设计冻结状态和撤销冻结的操作,补偿操作往往要比冻结操作容易实现得多。
SAGA 事务通常也不会直接靠裸编码来实现,一般也是在事务中间件的基础上完成,前面提到的 Seata 就同样支持 SAGA 事务模式。

引用资料

CSDN博主「zhouym
原文链接:[https://blog.csdn.net/zhouym
/article/details/90381606](https://blog.csdn.net/zhouym_/article/details/90381606)
百度:
https://baijiahao.baidu.com/s?id=1713687970654498813&wfr=spider&for=pc
CSDN博主「haitaoss」
原文链接:https://blog.csdn.net/lsswshxcg/article/details/108052544
CSDN博主「yutao_517」
原文链接:https://blog.csdn.net/weixin_46415378/article/details/123976008
CSDN博主「七路灯」
原文链接:https://blog.csdn.net/lihaipeng0417/article/details/120054294