SpringCloudAlibaba Seata分布式事务

分布式事务

事务是数据库的概念,数据库事务(ACID:原子性、一致性、隔离性和持久性);

分布式事务的产生,是由于数据库的拆分和分布式架构(微服务)带来的,在常规情况下,我们在一个进程中操作一个数据库,这属于本地事务,如果在一个进程(java程序)中操作多个数据库,或者在多个进程中操作一个或多个数据库,就产生了分布式事务;

(1)数据库分库分表就产生了分布式事务;

在这里插入图片描述

(2)项目拆分服务化也产生了分布式事务;

在这里插入图片描述

Seata介绍

Seata是一款开源的分布式事务解决方案,致力于在微服务架构下提供高性能和简单易用的分布式事务服务;

Seata为用户提供了AT、TCC、SAGA和XA事务模式,为用户打造一站式的分布式解决方案;

目前使用的流行度情况是:AT > TCC > Saga > XA;XA的流行度是我自己编的。。。

  • XA模式:强一致性分阶段事务模式,牺牲了一定的可用性,无业务侵入
  • AT模式:最终一致的分阶段事务模式,无业务侵入,也是Seata的默认模式
  • TCC模式:最终一致的分阶段事务模式,有业务侵入
  • SAGA模式:长事务模式,有业务侵入

无论哪种方案,都离不开TC,也就是事务的协调者。

我们可以参看seata各公司使用列表:
https://github.com/seata/seata/issues/1246 大部分公司都采用的AT事务模式;

Seata已经在国内很多团队开始落地,其中不乏有大公司;

Github:https://github.com/seata/seata
官网:http://seata.io/
当前最新版本:1.3.0

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对应的自己的本地事务进行提交还是回滚;

TC就是事务管理器,RM就是每个数据库连接(或者说本地事务),TM就是事务的入口(或者说事务的发起方),当然,它本身也是一个RM

每个微服务都需要有一个undo_log表,用于自动补偿,例如一个事务已经提交了,但这时发生了异常,此时该事务已经无法回滚了。
在提交事务之前,会查询一下要执行事务涉及到的表数据,并记录在undo_log表中,如果发生了上面的情况,则将数据自动改回去,而不需要我们敲代码来执行这个操作。

AT模式事务案例

注意

在一次面试的过程中,面试官说AT模式在线上测试,特别容易出现脏数据。

这个只能以后有机会再来验证了,到底有多容易出现脏数据。

单体应用多数据源分布式事务

在这里插入图片描述

在Spring Boot单体项目中,如果使用了多数据源,就需要考虑多个数据源的数据一致性,即产生了分布式事务的问题,我们采用Seata的AT事务模式来解决该分布式事务问题;

以电商购物下单为例:
在这里插入图片描述

  1. 准备数据库表和数据;

其中每个库中的undo_log表,是 Seata AT模式必须创建的表,主要用于分支事务的回滚;

  1. 开发一个SpringBoot单体应用

注意:以下代码并没有自己跑通过,是视频老师的

  • dynamic-datasource-spring-boot-starter 多数据源
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.bjpowernode</groupId>
    <artifactId>29-seata-distributed-transaction</artifactId>
    <version>1.0.0</version>

    <name>29-seata-distributed-transaction</name>
    <description>29-seata-distributed-transaction project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <spring-boot.version>2.3.0.RELEASE</spring-boot.version>
        <spring-cloud-alibaba.version>2.2.1.RELEASE</spring-cloud-alibaba.version>
    </properties>

    <dependencies>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>

        <!-- mysql-connector-java -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>

        <!-- mybatis-spring-boot-starter -->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.1.3</version>
        </dependency>

        <!-- seata-spring-boot-starter -->
        <dependency>
            <groupId>io.seata</groupId>
            <artifactId>seata-spring-boot-starter</artifactId>
            <version>1.3.0</version>
        </dependency>

        <!--
            dynamic-datasource-spring-boot-starter动态数据源

            mybatis-plus的作者写的这个

            作用:在一个项目中可以连接多个数据库(多数据源)


        -->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>dynamic-datasource-spring-boot-starter</artifactId>
            <version>3.2.0</version>
        </dependency>

        <!-- nacos-client -->
        <dependency>
            <groupId>com.alibaba.nacos</groupId>
            <artifactId>nacos-client</artifactId>
            <version>1.3.1</version>
        </dependency>

    </dependencies>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>${spring-boot.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-alibaba-dependencies</artifactId>
                <version>${spring-cloud-alibaba.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.1</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                    <encoding>UTF-8</encoding>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
            <!--mybatis代码自动生成插件-->
            <plugin>
                <groupId>org.mybatis.generator</groupId>
                <artifactId>mybatis-generator-maven-plugin</artifactId>
                <version>1.4.0</version>
                <configuration>
                    <!--配置文件的位置-->
                    <configurationFile>src/main/resources/generatorConfig.xml</configurationFile>
                    <!--生成代码过程中是否打印日志-->
                    <verbose>true</verbose>
                    <!--生成时是否覆盖java文件,xml文件总是合并-->
                    <overwrite>true</overwrite>
                </configuration>
            </plugin>
        </plugins>

        <resources>
            <resource>
                <directory>src/main/java</directory>
                <includes>
                    <include>**/*.xml</include>
                </includes>
            </resource>
            <resource>
                <directory>src/main/resources</directory>
                <includes>
                    <include>**/*.*</include>
                </includes>
            </resource>
        </resources>
    </build>

</project>
# 服务端口号
server.port=8080

# 应用服务名称
spring.application.name=29-seata-distributed-transaction

# 设置默认的数据源或者数据源组,default master
spring.datasource.dynamic.primary=order-ds

# order数据源配置
spring.datasource.dynamic.datasource.order-ds.url=jdbc:mysql://39.99.163.122:3306/orderdb?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf8&rewriteBatchedStatements=true&useSSL=false
spring.datasource.dynamic.datasource.order-ds.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.dynamic.datasource.order-ds.username=mysql
spring.datasource.dynamic.datasource.order-ds.password=UoT1R8[09/VsfXoO5>6YteB

# product数据源配置
spring.datasource.dynamic.datasource.product-ds.url=jdbc:mysql://39.99.163.122:3306/productdb?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf8&rewriteBatchedStatements=true&useSSL=false
spring.datasource.dynamic.datasource.product-ds.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.dynamic.datasource.product-ds.username=mysql
spring.datasource.dynamic.datasource.product-ds.password=UoT1R8[09/VsfXoO5>6YteB

# account数据源配置
spring.datasource.dynamic.datasource.account-ds.url=jdbc:mysql://39.99.163.122:3306/accountdb?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf8&rewriteBatchedStatements=true&useSSL=false
spring.datasource.dynamic.datasource.account-ds.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.dynamic.datasource.account-ds.username=mysql
spring.datasource.dynamic.datasource.account-ds.password=UoT1R8[09/VsfXoO5>6YteB

# 是否启动对seata的集成
spring.datasource.dynamic.seata=true

# Seata应用编号 default ${spring.application.name}
seata.application-id=${
   spring.application.name}
# Seata事务组编号,用于TC集群名
seata.tx-service-group=${
   spring.application.name}-group
# 虚拟组和分组的映射
# 29-seata-distributed-transaction-group 对应的就是 Seata事务组编号
#   也可以尝试用 ${seata.tx-service-group} 来获取
seata.service.vgroup-mapping.29-seata-distributed-transaction-group=default
# 这个default 对应的就是 上面那条的 default
seata.service.grouplist.default=192.168.172.128:8091
  • @DS(value = “order-ds”)

动态切换数据源就是依靠这个注解

  • @GlobalTransactional,事务的入口

还可以用在类上,就相当于给类上的每一个方法都加上了@GlobalTransactional

  • @Transactional,事务的分支,这个不用加也可以?有待考证 todo
package com.bjpowernode.service.impl;

import com.baomidou.dynamic.datasource.annotation.DS;
import com.bjpowernode.mapper.OrdersMapper;
import com.bjpowernode.model.Orders;
import com.bjpowernode.model.Product;
import com
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值