彻底掌握分布式事务2PC、3PC模型

结言

什么是事务

=====

事务是数据库操作的最小工作单元,一组不可再分割的操作集合,是作为单个逻辑工作单元执行的一系列操作。这些操作作为一个整体一起向系统提交,要么都执行、要么都不执行

事务具有四个特征,分别是原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)和持久性(Durability),简称为事务的 ACID 特性

如何保证事务的 ACID 特性?

  • 原子性(Atomicity):事务内 SQL 要么同时成功要么同时失败,基于撤销日志(undo 日志)实现

  • 一致性(Consistency):系统从一个正确态转移到另一个正确态,由应用通过 AID 来保证,可以说是事务的核心特性

  • 隔离性(Isolation):控制事务并发执行时数据的可见性,基于锁和多版本并发控制(mvcc)实现

  • 持久性(Durability):提交后一定存储成功不会丢失,基于重做日志(redo log)实现

文章主要是介绍分布式事务 2PC 和 3PC,关于 redo、undo 日志、mvcc、锁这块的内容后续再详细介绍

在早些时候,我们应用程序还是单体项目,所以操作的都是单一数据库,这种情况下我们称之为本地事务。本地事务的 ACID 一般都是由数据库层面支持的,比如我们工作中常用的 MySQL 数据库

彻底掌握分布式事务2PC、3PC模型

平常我们在操作 MySQL 客户端时,MySQL 会隐式对事务做自动提交,所以日常工作不会涉及手动书写事务的创建、提交、回滚等操作。如果想要试验锁、MVCC等特性,可以创建多个会话,通过begin、commit、rollback等命令来试验下不同事务之间的数据,看执行结果和自己所想是否一致

彻底掌握分布式事务2PC、3PC模型

我们平常开发项目代码时使用的是 Spring 封装好的事务,所以也不会手动编写对数据库事务的提交、回滚等方法(个别情况除外)。这里使用原生 JDBC 写一个示例代码,帮助大家理解如何通过事务保证 ACID 四大特性

Connection conn = …; // 获取数据库连接

conn.setAutoCommit(false); // 开启事务

try {

// …执行增删改查sql

conn.commit(); // 提交事务

} catch (Exception e) {

conn.rollback(); // 事务回滚

} finally {

conn.close(); // 关闭链接

}

设想一下,每次进行数据库操作,都要写重复的创建事务、提交、回滚等方法是不是挺痛苦的,那 Spring 如何自动帮助我们管理事务的呢?Spring 项目中我们一般使用两种方式来进行事务的管理,编程式事务和声明式事务

项目中使用 Spring 管理事务,要么在接口方法上添加注解 @Transactional,要么使用 AOP 配置切面事务。其实这两种方式大同小异,只不过 @Transactional 的粒度更细一些,实现原理上都是依赖 AOP,举例说明下

@Service

public class TransactionalService {

@Transactional

public void save() {

// 业务操作

}

}

TransactionalService 会被 Spring 创建一个代理对象放入到容器中,创建后的代理对象相当于下述类

public class TransactionalServiceProxy {

private TransactionalService transactionalService;

public TransactionalServiceProxy(TransactionalService transactionalService) {

this.transactionalService = transactionalService;

}

public void save() {

try {

// 开启事务操作

transactionalService.save();

} catch (Exception e) {

// 出现异常则进行回滚

}

// 提交事务

}

}

示例代码看着简洁明了,但是真正的代码生成代码对比要复杂很多。关于事务管理器,Spring 提供了接口 PlatformTransactionManager,其内部包含两个重要实现类

  • DataSourceTransactionManager:支持本地事务,内部通过java.sql.Connection来开启、提交和回滚事务

  • JtaTransactionManager:用于支持分布式事务,其实现了 JTA 规范,使用 XA 协议进行两阶段提交

通过这两个实现类得知,平常我们使用的编程式事务和声明式事务依赖于本地事务管理实现,Spring 同时也支持分布式事务,关于 JTA 分布式事务的支持网上资料挺多的,就不在这里赘述了

什么是分布式事务

========

日常业务代码中的本地事务我们一直都在用,理解起来并不困难。但是随着服务化(SOA)、微服务的流行,平常我们的单一业务系统被拆分成为了多个系统,为了迎合业务系统的变更,数据库也结合业务进行了拆分

比如以学校管理系统举例说明,可能就会拆分为学生服务、课程服务、老师服务等,数据库也拆分为多个库。当这种情况,把不同的服务部署到服务器,就会有可能面临下述的服务调用

彻底掌握分布式事务2PC、3PC模型

ServiceA 服务需要操作数据库执行本地事务,同时需要调用 ServiceB 和 ServiceC 服务发起事务调用,如何保证三个服务的事务要么一起成功或者一起失败,如何保证用户发起请求的事务 ACID 特性呢?无疑这就是分布式事务场景,三个服务的单一本地事务都无法保证整个请求的事务

分布式事务场景有很多种解决方案,以不同分类来看,强一致性解决方案、最终一致性解决方案,细分其中的方案包括2PC、3PC、TCC、可靠消息…

业界中使用较多的像阿里的 RocketMQ 事务消息、Seata XA模式、可靠消息模型这几种解决方案。不过,分布式事务无一例外都是会直接或间接操作多个数据库,而且使用了分布式事务同时也会带来新的挑战,那就是性能问题。如果为了保证强一致性分布式事务亦或者补偿方案的最终一致性,导致了性能的下降,对于正常业务而言,无疑是得不偿失的

DTP 模型和 XA 规范

=============

X/Open 组织定义了分布式事务的模型(DTP)和 分布式事务协议(XA),DTP 由以下几个模型元素组成

  • AP(Application 应用程序):用于定义事务边界(即定义事务的开始和结束),并且在事务边界内对资源进行操作

  • TM(Transaction Manager 事务管理器):负责分配事务唯一标识,监控事务的执行进度,并负责事务的提交、回滚等

  • RM(Resource Manager 资源管理器):如数据库、文件系统等,并提供访问资源的方式

  • CRM(Communication Resource Manager 通信资源管理器):控制一个TM域(TM domain)内或者跨TM域的分布式应用之间的通信

  • CP(Communication Protocol 通信协议):提供CRM提供的分布式应用节点之间的底层通信服务

在 DTP 分布式事务模型中,基本组成需要涵盖 AP、TM、RMS(不需要 CRM、CP 也是可以的),如下图所示

彻底掌握分布式事务2PC、3PC模型

XA 规范

=====

XA 规范最重要的作用就是定义 RM(资源管理器)与 TM(事务管理器)之间的交互接口。另外,XA 规范除了定义 2PC 之间的交互接口外,同时对 2PC 进行了优化

彻底掌握分布式事务2PC、3PC模型

梳理下 DTP、XA、2PC 之间的关系

DTP 规定了分布式事务中的角色模型,并在其中指定了全局事务的控制需要使用 2PC 协议来保证数据的一致性

2PC 是 Two-Phase Commit 的缩写,即二阶段提交,是计算机网络尤其是数据库领域内,为了保证分布式系统架构下所有节点在进行事务处理过程中能够保证原子性和一致性而设计的一种算法。同时,2PC 也被认为是一种一致性协议,用来保证分布式系统数据的一致性

XA 规范是 X/Open 组织提出的分布式事务处理规范,XA 规范定义了 2PC(两阶段提交协议)中需要用到的接口,也就是上图中 RM 和 TM 之间的交互。2PC 和 XA 两者最容易混淆,可以这么理解,DTP 模型定义 TM 和 RM 之间通讯的接口规范叫 XA,然后 关系数据库(比如MySQL)基于 X/Open 提出的的 XA 规范(核心依赖于 2PC 算法)被称为 XA 方案

2PC 一致性算法

=========

当应用程序(AP)发起一个事务操作需要跨越多个分布式节点的时候,每一个分布式节点(RM)知道自己进行事务操作的结果是成功或是失败,但是却不能获取到其它分布式节点的操作结果。为了保证事务处理的 ACID 特性,就需要引入称为"协调者"的组件(TM)来进行统一调度分布式的执行逻辑

协调者负责调度参与整体事务的分布式节点的行为,并最终决定这些分布式节点要把事务进行提交还是回滚。所以,基于这种思想下,衍生出了二阶段提交和三阶段提交两种分布式一致性算法协议。二阶段指的是准备阶段和提交阶段,下面我们先看准备阶段都做了什么事情

2PC-准备阶段

========

二阶段提交中第一阶段也叫做"投票阶段",即各参与者投票表明自身是否继续执行接下来的事务提交步骤

  • 事务询问:协调者向所有参与本次分布式事务的参与者发送事务内容,询问是否可以执行事务提交操作,然后开始等待各个参与者的响应

  • 执行事务:参与者收到协调者的事务请求,执行对应的事务,并将内容写入 Undo 和 Redo 日志

  • 返回响应:如果各个参与者执行了事务,那么反馈协调者 Yes 响应;如果各个参与者没有能够成功执行事务,那么就会返回协调者 No 响应

如果第一阶段全部参与者返回成功响应,那么进入事务提交步骤,反之本次分布式事务以失败返回。以 MySQL 数据库为例,在第一阶段,事务管理器(TM)向所有涉及到的数据库(RM)发出 prepare(准备提交) 请求,数据库收到请求后执行数据修改和日志记录处理,处理完成后把事务的状态修改为 “可提交”,最终将结果返回给事务处理器

2PC-提交阶段

========

提交阶段分为两个流程,一个是各参与者正常执行事务提交流程,并返回 Yes 响应,表示各参与者投票执行成功;一个是各参与者当中有执行失败返回 No 响应或超时情况,将触发全局回滚,表示分布式事务执行失败

  • 执行事务提交

  • 中断事务

执行事务提交

======

假设协调者从所有的参与者获得的反馈都是 Yes 响应,那么就会执行事务提交操作

  • 事务提交:协调者向所有参与者节点发出 Commit 请求,各个参与者接收到 Commit 请求后,将本地事务进行提交操作,并在完成提交之后释放事务执行周期内占用的事务资源

  • 完成事务:各个参与者完成事务提交之后,向协调者发送 Ack 响应,协调者接收到响应后完成本次分布式事务

彻底掌握分布式事务2PC、3PC模型

中断事务

====

假设任意一个事务参与者节点向协调者反馈了 No 响应(注意这里的 No 响应指的是第一阶段),或者在等待超时之后,协调者没有接到所有参与者的反馈响应,那么就会进行事务中断流程

  • 事务回滚:协调者向所有参与者发出 Rollback 请求,参与者接收到回滚请求后,使用第一阶段写入的 undo log 执行事务的回滚,并在完成回滚事务之后释放占用的资源

  • 中断事务:参与者在完成事务回滚之后,向协调者发送 Ack 消息,协调者接收到事务参与者的 Ack 消息之后,完成事务中断

彻底掌握分布式事务2PC、3PC模型

2PC 优缺点

=======

2PC 提交将事务的处理过程分为了投票和执行两个阶段,核心思想就是对每个事务都采用先尝试后提交的方式处理。2PC 优点显而易见,那就是 原理简单,实现方便。简单也意味着很多地方不能尽善尽美,这里梳理三个比较核心的缺陷

  1. 同步阻塞:无论是在第一阶段的过程中,还是在第二阶段,所有的参与者资源和协调者资源都是被锁住的,只有当所有节点准备完毕,事务协调者才会通知进行全局提交,参与者进行本地事务提交后才会释放资源。这样的过程会比较漫长,对性能影响比较大

  2. 单点故障:如果协调者出现问题,那么整个二阶段提交流程将无法运转。另外,如果协调者是在第二阶段出现了故障,那么其它参与者将会处于锁定事务资源的状态中

  3. 数据不一致性:当协调者在第二阶段向所有参与者发送 Commit 请求后,发生了局部网络异常或者协调者在尚未发送完 Commit 请求之前自身发生了崩溃,导致只有部分参与者接收到 Commit 请求,那么接收到的参与者就会进行提交事务,进而形成了数据不一致性

由于 2PC 的简单方便,所以会产生上述的同步阻塞、单点故障、数据不一致等情况,所以在 2PC 的基础上做了改进,推行出了三阶段提交(3PC)

使用 2PC 存在诸多限制,首先就是数据库需要支持 XA 规范,而且性能与数据一致性数据均不友好,所以 Seata 中虽然支持 XA 模式,但是主推的还是 AT 模式

3PC 一致性算法

=========

三阶段提交(3PC)是二阶段提交(2PC)的一个改良版本,引入了两个新的特性

  1. 协调者和参与者均引入超时机制,通过超时机制来解决 2PC 的同步阻塞问题,避免事务资源被永久锁定

  2. 把二阶段演变为三阶段,二阶段提交协议中的第一阶段"准备阶段"一分为二,形成了新的 CanCommit、PreCommit、do Commit 三个阶段组成事务处理协议

彻底掌握分布式事务2PC、3PC模型

这里将不再赘述 3PC 的详细提交过程,3PC 相比较于 2PC 最大的优点就是降低了参与者的阻塞范围,并且能够在协调者出现单点故障后继续达成一致

虽然通过超时机制解决了资源永久阻塞的问题,但是 3PC 依然存在数据不一致的问题。当参与者接收到 PreCommit 消息后,如果网络出现分区,此时协调者与参与者无法进行正常通信,这种情况下,参与者依然会进行事务的提交

通过了解 2PC 和 3PC 之后,我们可以知道这两者都无法彻底解决分布式下的数据一致性

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)

img

最后

手绘了下图所示的kafka知识大纲流程图(xmind文件不能上传,导出图片展现),但都可提供源文件给每位爱学习的朋友

image.png

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!
学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!**

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)

img

最后

手绘了下图所示的kafka知识大纲流程图(xmind文件不能上传,导出图片展现),但都可提供源文件给每位爱学习的朋友

[外链图片转存中…(img-lCSStmh6-1713680352350)]

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

  • 8
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值