在分布式系统中,一个事务的多个参与者在不同的分布式节点上,这些参与者可以操作同一个数据源也可以操作不同的数据源,这种情况产生的事务都称为分布式事务。
注意:多个参与者必须不在同一个节点(项目)中,才能叫做分布式事务。如果所有参数者都在一个项目中,这种事务称为本地事务。
例如:
学生管理系统实现tb_student表的新增,电话号管理系统实现tb_phone_no表的新增,现在要完成新增学生的功能,需要在学生管理系统中新增数据到tb_student表格、在电话号管理系统中新增数据到tb_phone_no表格,要求新增数据的时候,必须同时成功或同时失败。两个分布式系统的新增方法组成一个事务,这个事务就是分布式事务,同样要保证事务的ACID特性。
例如:
两个项目,其中一个向MySQL做新增,另一个需要向Redis执行新增,希望访问MySQL或Redis时如果出现异常时同时进行事务回滚,这种情况也称为分布式事务。
事务的概念最早是在学习数据库MySQL时接触到的,一个事务(本地事务)就是一系列SQL语句的集合,只要在执行过程中一条SQL出错就会导致整个事务失败,回滚到原点。
而在分布式系统中存在多模块完成同一个业务。那么就存在一个业务由多模块操作同一个数据源。甚至多模块操作多种数据源的可能。这些问题都可以由分布式事务解决方案TX-LCN解决。
简而言之,只要出现了分布式事务,就必须使用分布式事务管理。
分布式事务问题在互联网公司是比较常见的。分布式事务解决方案也有很多种:
著名的分布式事务管理框架:
分布式事务存在两大理论依据:CAP定理和BASE理论。
CAP定理是指在一个分布式系统中Consistency(一致性)、Availability(可用性)、Partition tolerance(分区容错性),最多同时满足其中两个,三者不可兼得。
在分布式系统中所有节点的状态是一样的。
在集群中一部分节点出现故障后,整个集群是否还能响应客户端请求。
以实际效果而言,分区相当于对操作的时限要求。如果系统不能在一定时限内达到数据一致性,就意味着发生了分区的情况,此时就必须在A和C中做选择。
是指Basically Available(基本可用)、Soft state(软状态)和Eventually consistent(最终一致性)三个短语的缩写。 BASE理论是对CAP中一致性和可用性权衡的结果,是基于CAP演化而来的。
BASE理论核心思想:即使无法做到强一致性,每个应用都可以根据自身业务特点,采用适当的方式达到最终一致性。
是指在分布式系统中出现不可知故障的时候,允许损失部分可用性。此处要注意:损失部分可用性,不代表整个系统不可用。
例如:
是指系统中数据允许存在中间状态(软状态),并认为这个状态是不影响系统的可用性的。通俗解释:允许分布式节点之间存在同步延迟。
例如:
允许整个系统中数据在经过一定时间后,最终能达到整个系统的一致性。但是这个时间绝对不可以过长。
强一致性要求系统接收请求后,整个系统必须达到一致性效果,才会响应结果。
最终一致性是弱一致性的特例。满足最终一致性的系统在响应给用户结果时整个系统可能是没有达到一致性的,但是最终一定会达到一致性效果的。
LCN框架在2017年6月发布第一个版本,目前最新已经达到5.0版本。LCN早期设计时,1.0版本和2.0版本设计步骤如下:
锁定事务单元(Lock)
确认事务模块状态(Confirm)
通知事务(Notify)
取各自首字母后命名为LCN。LCN框架从5.0开始兼容了LCN、TCC、TXC三种事务模式,为了和LCN框架区分,从5.0开始把LCN框架更名为:TX-LCN分布式事务框架。
TX-LCN由两大模块组成,TxClient、TxManager。
TxClient作为模块的依赖框架,提供了TX-LCN的标准支持,事务发起方和参与方都属于TxClient。TxManager作为分布式事务的控制方,控制整个事务。
是指在事务发起方开始执行业务代码之前先调用TxManager创建事务组对象,然后拿到事务标识GroupId的过程。
添加事务组是指参与方在执行完业务方法以后,将该模块的事务信息通知给TxManager的操作。
是指在发起方执行完业务代码以后,将发起方执行结果状态通知给TxManager,TxManager将根据事务最终状态和事务组的信息来通知相应的参与模块提交或回滚事务,并返回结果给事务发起方。
Tx-LCN 5.0开始支持三种事务模式,分别是:LCN、TCC、TXC模式。
每种模式在实际使用时有着自己对应的注解。
LCN模式是通过代理JDBC中Connection的方式实现对本地事务的操作,然后在由TxManager统一协调控制事务。当本地事务提交回滚或者关闭连接时将会执行假操作,该代理的连接将由LCN连接池管理。
该模式对代码的侵入性低。
该模式仅限于本地存在连接对象且可通过连接对象控制事务的模块。
该模式下的事务提交与回滚是由本地事务方控制,对于数据一致性上有较高的保障。
该模式缺陷在于代理的连接需要随事务发起方一共释放连接,增加了连接占用的时间。
总结:LCN模式适合能用JDBC连接的所有支持事务的数据库
TCC事务机制相对于传统事务机制(X/Open XA Two-Phase-Commit),其特征在于它不依赖资源管理器(RM)对XA的支持,而是通过对(由业务系统提供的)业务逻辑的调度来实现分布式事务。主要由三步操作,Try:尝试执行业务(添加了@TccTransaction注解的方法)、 Confirm:确认执行业务(确认方法作用编写提交事务的代码。当分布式事务执行成功时执行的方法,此方法需要程序员自己提供,自己维护。对于Mongo或Redis这种不支持事务的数据库都不去写此方法。)、 Cancel:取消执行业务(需要程序员自己编写,自己维护,编写回滚事务的逻辑)。
该模式对代码的侵入性高,要求每个业务需要写二个以上步骤的操作。
该模式对有无本地事务控制都可以支持,使用面广。
数据一致性控制几乎完全由开发者控制,对业务开发难度要求高。
总结:Tcc模式应用于所有不支持XA事务的软件。例如:Redis,Elasticsearch等。
TXC模式命名来源于淘宝,实现原理是在执行SQL之前,先查询SQL的影响数据,然后保存执行的SQL信息和创建锁。当需要回滚的时候就采用这些记录数据回滚数据库,目前锁实现依赖Redis分布式锁控制。(在使用TX-LCN时必须要配置Redis参数)
该模式同样对代码的嵌入性低。
该模式仅限于对支持SQL方式的模块支持。
该模式由于每次执行SQL之前需要先查询影响数据,因此相比LCN模式消耗资源与时间要多。
该模式不会占用数据库的连接资源。
总结:只能用在支持SQL的数据库。对资源消耗较多。建议使用LCN模式。
现在很多数据库本身支持分布式事务处理。既然数据库支持分布式事务,为什么还要使用TX-LCN分布式事务管理框架?这就要说说数据库本身支持的分布式事务解决方案的特性了。
XA协议由Oracle Tuxedo首先提出的,并交给X/Open组织,作为资源管理器(数据库)与事务管理器的接口标准。目前,Oracle、Informix、DB2和Sybase等各大数据库厂家都提供对XA的支持。XA协议采用两阶段提交方式来管理分布式事务。XA接口提供资源管理器与事务管理器之间进行通信的标准接口。
XA就是X/Open DTP定义的交易中间件与数据库之间的接口规范(即接口函数),交易中间件用它来通知数据库事务的开始、结束以及提交、回滚等。XA接口函数由数据库厂商提供。
X/Open组织(即现在的Open Group)定义了分布式事务处理模型。X/Open DTP模型(1994)包括应用程序(AP)、事务管理器(TM)、资源管理器(RM)、通信资源管理器(CRM)四部分。一般,常见的事务管理器(TM)是交易中间件,常见的资源管理器(RM)是数据库,常见的通信资源管理器(CRM)是消息中间件。
如果在程序中开启了事务,那么在应用程序发出提交/回滚请求后,数据库执行操作,而后将成功/失败返回给应用程序,程序继续执行。
一阶段提交协议相对简单。优点也很直观,它不用再与其他的对象交互,节省了判断步骤和时间,所以在性能上是在阶段提交协议中最好的。但缺点也很明显:数据库确认执行事务的时间较长,出问题的可能性就随之增大。如果有多个数据源,一阶段提交协议无法协调他们之间的关系。
在一阶段协议的基础上,有了二阶段协议,二阶段协议的好处是添加了一个管理者角色。
很明显,二阶段协议通过将两层变为三层,增加了中间的管理者角色,从而协调多个数据源之间的关系,二阶段提交协议分为两个阶段。
应用程序调用了事务管理器的提交方法,此后第一阶段分为两个步骤:
事务管理器通知参与该事务的各个资源管理器,通知他们开始准备事务。
资源管理器接收到消息后开始准备阶段,写好事务日志并执行事务,但不提交,然后将是否就绪的消息返回给事务管理器(此时已经将事务的大部分事情做完,以后的内容耗时极小)。
第二阶段也分为两个步骤:
事务管理器在接受各个消息后,开始分析,如果有任意其一失败,则发送回滚命令,否则发送提交命令。各个资源管理器接收到命令后,执行(耗时很少),并将提交消息返回给事务管理器。
事务管理器接受消息后,事务结束,应用程序继续执行。
为什么要分两步执行?一是因为分两步,就有了事务管理器统一管理的机会;二尽可能晚地提交事务,让事务在提交前尽可能地完成所有能完成的工作,这样,最后的提交阶段将是耗时极短,耗时极短意味着操作失败的可能性也就降低。
同时,二阶段提交协议为了保证事务的一致性,不管是事务管理器还是各个资源管理器,每执行一步操作,都会记录日志,为出现故障后的恢复准备依据。
创建父工程txlcn_parent
<parent>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-parentartifactId>
<version>2.3.12.RELEASEversion>
parent>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-dependenciesartifactId>
<version>Hoxton.SR12version>
<scope>importscope>
<type>pomtype>
dependency>
<dependency>
<groupId>com.codingapi.txlcngroupId>
<artifactId>txlcn-tmartifactId>
<version>5.0.2.RELEASEversion>
dependency>
<dependency>
<groupId>com.codingapi.txlcngroupId>
<artifactId>txlcn-tcartifactId>
<version>5.0.2.RELEASEversion>
dependency>
<dependency>
<groupId>com.codingapi.txlcngroupId>
<artifactId>txlcn-txmsg-nettyartifactId>
<version>5.0.2.RELEASEversion>
dependency>
<dependency>
<groupId>org.mybatis.spring.bootgroupId>
<artifactId>mybatis-spring-boot-starterartifactId>
<version>2.2.0version>
dependency>
dependencies>
dependencyManagement>
创建工程txlcn_tm
<dependencies>
<dependency>
<groupId>com.codingapi.txlcngroupId>
<artifactId>txlcn-tmartifactId>
dependency>
dependencies>
编写配置文件application.properties。
spring.application.name=TransactionManager
server.port=7970
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/tx-manager?characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
spring.datasource.username=root
spring.datasource.password=root
# redis配置
spring.redis.host=192.168.8.128
# TM服务器IP修改, TC访问TM时,使用的IP地址。必须精确匹配。默认127.0.0.1。代表TC和TM必须在同一个主机中。
tx-lcn.manager.host=192.168.41.252
# TM服务器日志系统配置,默认关闭日志系统。需要独立配置日志的存储数据库连接
tx-lcn.logger.enabled=false
# 配置TM服务器日志系统数据库连接
tx-lcn.logger.driver-class-name=com.mysql.cj.jdbc.Driver
tx-lcn.logger.jdbc-url=jdbc:mysql://localhost:3306/tx-manager?characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
tx-lcn.logger.username=root
tx-lcn.logger.password=root
# 自动创建表格的配置项。可以自动创建日志表格。
spring.jpa.hibernate.ddl-auto=update
# 修改TM服务器的WEB控制台登录密码, 默认登录密码是 codingapi
tx-lcn.manager.admin-key=wck
# TM事务管理端口。默认 0. 是server.port + 100计算得到。
# tx-lcn.manager.port=7971
在依赖的资源txlcn-tm中,其API资源包中包含默认配置文件application.properties。具体如下:
因Spring Boot读取配置文件优先级,索引项目中必须使用properties配置文件,否则无法正常读取自定义配置。
/**
* EnableTransactionManagerServer - 开启TM服务器功能。
*
* 必须提供配置文件。注意:配置文件只能是properties格式。
* 原因:txlcn-tm中包含一个配置文件,命名是application.properties。
* 读取优先级高于yml配置文件。所以定义yml配置文件,会被忽略。
*
*/
@SpringBootApplication
@EnableTransactionManagerServer
public class MyTMApp {
public static void main(String[] args) {
SpringApplication.run(MyTMApp.class, args);
}
}
在依赖的资源txlcn-tm中,包含TxManager需要的数据库SQL脚本文件tx-manager.sql,可以查找并执行此脚本为TxManager创建需要的数据库表格。
脚本文件内容如下:
CREATE DATABASE IF NOT EXISTS `tx-manager` DEFAULT CHARSET utf8 COLLATE utf8_general_ci;
USE `tx-manager`