ShardingSphere分布式事务

1. 本地事务

大多数场景下,我们的应用都只需要操作单一的数据库,这种情况下的事务称之为本地事务 (Local Transaction)。本地事务的ACID特性是数据库直接提供支持。本地事务应用架构如下所示:

在JDBC编程中,我们通过java.sql.Connection对象来开启、关闭或者提交事务。代码如下所示:

1 Connection conn = ... //获取数据库连接 
2 conn.setAutoCommit(false); //开启事务 
3 try{ 
4 //...执行增删改查sql 
5 conn.commit(); //提交事务 
6 }catch (Exception e) { 
7 conn.rollback();//事务回滚 
8 }finally{ 
9 conn.close();//关闭链接 
10 }

2.分布式事务

分布式事务产生原因:

在微服务架构中,完成某一个业务功能可能需要横跨多个服务,操作多个数据库。这就涉及到到了分布式事务,需要操作的资源位于多个资源服务器上,而应用需要保证对于多个资源服务器的数据操作,要么全部成功,要么全部失败。本质上来说,分布式事务就是为了保证不同资源服务器的数据一致性。

使用本地事物能不能解决上述问题呢?

如果要解决上述问题,就需要做到在一个请求中先连接数据库A再切换连接为数据库B,

对于转账交易来说,A中的钱划扣了,那么就需要切换数据库连接为B,将B中的数据库的钱也划扣掉。如果B出现失败,两个数据库开始回滚。

典型分布式事务产生场景:

1) 跨库事务

跨库事务指的是,一个应用某个功能需要操作多个库,不同的库中存储不同的业务数据。下图演示了一个服务同时操作2个库的情况:

2) 分库分表

通常一个库数据量比较大或者预期未来的数据量比较大,都会进行分库分表。如下图,将数据库B拆分成了2个库:

对于分库分表的情况,一般开发人员都会使用一些数据库中间件来降低sql操作的复杂性。如,对于sql:insert into user(id,name) values (1,"张三"),(2,"李四")。这条sql是操作单库的语法,单库情况下,可以保证事务的一致性。 但是由于现在进行了分库分表,开发人员希望将1号记录插入分库1,2号记录插入分库2。所以数据库中间件要将其改写为2条sql,分别插入两个不同的分库,此时要保证两个库要不都成功,要不都失败,因此基本上所有的数据库中间件都面临着分布式事务的问题。

3) 微服务架构

下图演示了一个3个服务之间彼此调用的微服务架构

Service A完成某个功能需要直接操作数据库,同时需要调用Service B和Service C,而Service B又同时操作了2个数据库,Service C也操作了一个库。需要保证这些跨服务调用对多个数据库的操作要么都成功,要么都失败,实际上这可能是最典型的分布式事务场景。

小结:上述讨论的分布式事务场景中,无一例外的都直接或者间接的操作了多个数据库。如何保证事务的ACID特性,对于分布式事务实现方案而言,是非常大的挑战。同时,分布式事务实现方案还必须要考虑性能的问题,如果为了严格保证ACID特性,导致性能严重下降,那么对于一些要求快速响应的业务,是无法接受的。

 3. 两阶段提交

两阶段提交(Two Phase Commit),就是将提交(commit)过程划分为2个阶段(Phase):

阶段1:

TM通知各个RM准备提交它们的事务分支。如果RM判断自己进行的工作可以被提交,那就对工作内容进行持久化,再给TM肯定答复;要是发生了其他情况,那给TM的都是否定答复。 以mysql数据库为例,在第一阶段,事务管理器向所有涉及到的数据库服务器发出prepare"准备提交"请求,数据库收到请求后执行数据修改和日志记录等处理,处理完成后只是把事务的状态改成"可以提交",然后把结果返回给事务管理器。

阶段2

TM根据阶段1各个RM prepare的结果,决定是提交还是回滚事务。如果所有的RM都prepare成功,那么TM通知所有的RM进行提交;如果有RM prepare失败的话,则TM通知所有RM回滚自己的事务分支。

以mysql数据库为例,如果第一阶段中所有数据库都prepare成功,那么事务管理器向数据库服务器发出"确认提交"请求,数据库服务器把事务的"可以提交"状态改为"提交完成"状态,然后返回应答。如果在第一阶段内有任何一个数据库的操作发生了错误,或者事务管理器收不到某个数据库的回应,则认为事务失败,回撤所有数据库的事务。数据库服务器收不到第二阶段的确认提交请求,也会把"可以提交"的事务回撤。

两阶段提交方案下全局事务的ACID特性,是依赖于RM的。一个全局事务内部包含了多个独立的事务分支,这一组事务分支要么都成功,要么都失败。各个事务分支的ACID特性共同构成了全局事务的ACID特性。也就是将单个事务分支支持的ACID特性提升一个层次到分布式事务的范畴。

2PC存在的问题

同步阻塞问题

2PC 中的参与者是阻塞的。在第一阶段收到请求后就会预先锁定资源,一直到 commit 后才会释放。

单点故障

由于协调者的重要性,一旦协调者TM发生故障,参与者RM会一直阻塞下去。尤其在第二阶段,协调者发生故障,那么所有的参与者还都处于锁定事务资源的状态中,而无法继续完成事务操作。

数据不一致(最主要的问题)

若协调者第二阶段发送提交请求时崩溃,可能部分参与者收到commit请求提交了事务,而另一部分参与者未收到commit请求而放弃事务,从而造成数据不一致的问题。

关于2PC实现的分布式事务,可以参考我在Gitee上所编写的源码。multi_datasource: 多数据源框架,包含多数据源切换以及多数据源事务icon-default.png?t=O83Ahttps://gitee.com/jenkin1011/multi_datasource

4. Seata

在 Seata 的架构中,一共有三个角色:
TC (Transaction Coordinator) - 事务协调者
维护全局和分支事务的状态,驱动全局事务提交或回滚。
TM (Transaction Manager) - 事务管理器 定义全局事务的范围:开始全局事务、提交或回滚全局事务。
RM (Resource Manager) - 资源管理器
管理分支事务处理的资源,与TC交谈以注册分支事务和报告分支事务的状态,并驱动分支事务提交或回滚。 其中,TC 为单独部署的 Server 服务端,TM 和 RM 为嵌入到应用中的 Client 客户端。
在 Seata 中,一个分布式事务的生命周期如下:
1. TM 请求 TC 开启一个全局事务。 TC 会生成一个 XID 作为该全局事务的编号。XID会在微服
务的调用链路中传播,保证将多个微服务的子事务关联在一起。
2. RM 请求 TC 将本地事务注册为全局事务的分支事务 ,通过全局事务的 XID 进行关联。
3. TM 请求 TC 告诉 XID 对应的全局事务是进行提交还是回滚。
4. TC 驱动 RM 们将 XID 对应的自己的本地事务进行提交还是回滚。
在分库分表相关的分布式事务中,本人还是以2PC为实现分布式事务的基础,各位看官如果对系统健壮性有特别强的需求的话,比如要求数据正确率达到99.99%以上,可以自行研究Seata框架,以及ShardingSphere如何整合Seata。

5. ShardingJDBC分布式事务XA实现

XA是一种分布式事务规范,与之对应的是JAVA平台上的事务规范
JTA(Java Transaction Api)。JTA定义了对XA事务的支持,实际上,JTA
就是基于XA构建的。但是JTA只是相当于一组结构,定义了分布式事务
的处理方式,具体实现还是需要由各个厂商提供。
目前JTA有两种实现方式,一种是由特定的J2EE容器提供,例如这里提到
的 narayana 就是由JBOSS提供的。另一种就是适用于所有J2EE的通用
规范,例如Atomokios,他是ShardingSphere默认使用的事务管理器。
在ShardingJDBC的官方文档中,有对分布式事务的几个示例,可以用来参考
下:
https://shardingsphere.apache.org/document/legacy/4.x/document/cn/man
ual/sharding-jdbc/usage/transaction/
简单来说,在SpringBoot中分为以下几个步骤:
1、引入maven依赖
<dependency>
<groupId>org.apache.shardingsphere</groupId>
<artifactId>sharding-jdbc-core</artifactId>
<version>5.2.1</version>
</dependency>
<!-- 使用XA事务时,需要引入此模块 -->
<dependency>
<groupId>org.apache.shardingsphere</groupId>
<artifactId>sharding-transaction-xa-core</artifactId>
<version>4.1.1</version>
</dependency>
<!-- 使用XA事务时,可以引入其他几种事务管理器 -->
<dependency>
<groupId>org.apache.shardingsphere</groupId>
<artifactId>shardingsphere-transaction-xa-bitronix</artifactId>
</dependency>
<dependency>
<groupId>org.apache.shardingsphere</groupId>
<artifactId>shardingsphere-transaction-xa-narayana</artifactId>
</dependency>
2、配置事务管理器
@Configuration
@EnableTransactionManagement
public class TransactionConfiguration {
@Bean
public PlatformTransactionManager txManager(final DataSource
dataSource) {
return new DataSourceTransactionManager(dataSource);
}
//如果不使用jdbctemplate就可以不注入。
@Bean
public JdbcTemplate jdbcTemplate(final DataSource dataSource) {
return new JdbcTemplate(dataSource);
}
}
使用分布式事务管理器的重点是两个地方,一是配置
@EnableTransactionManagement注解,启用事务管理;二是注入
TransactionManager对象,其中对于这个事务管理器的重点就是要使用
ShardingDatasource。
3、在业务代码中使用
    @Transactional
//    @ShardingTransactionType(TransactionType.XA)
    public void combineDatasource(Order order) {
        // maintain user
        userInfoRepository.maintainUser(order);

        // insert database
        orderInfoRepository.createOrder(order);
    }
ShardingSphere默认是使用的Atomikos作为XA事务管理器,在项目中会生成一
个xa_tx.log,这个是XA崩溃恢复所需的日志,不要删除。另外,可以在项目的
classpath中添加jta.properties来定制Atomikos的配置项。具体配置项参见 http
s://www.atomikos.com/Documentation/JtaProperties
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值