使用seata实现分布式事务

分布式事务

一. CAP定理

CAP定理是指导分布式环境下,分布式事务的处理一个理论:两者只能满足其二

C(Consistency): 一致性(强一致性)。

A(Availability): 可用性。

P: (Partition tolerance): 分区容忍性(允许分布式环境下,多个系统间可以失去联系)。

可用性是任何一个项目必须要保证的;分区容忍性就是项目设计成分布式项目,就已经满足这一条;所以我们牺牲的是一致性,不是说不保证事务的一致性,不是强一致性,我们需要保证的是最终的一致性。

二. BASE理论

BA(Basically Available): 基本可用,因为没有事务的强一致,在实际的场景下并不满足用户的想要的结果。

Soft state(软状态): 中间状态。

Eventually consistent:最终一致性。

三. 解决方案

3.1 RabbitMQ的分布式事务解决

3.2 二阶段提交

二阶段提交和三阶段提交都是隶属于 XA协议 是DBMS的一种分布式事务的协议。

二阶段事务提交和Mysql的本身的设计是有关系的。在介绍两阶段提交之前,先必须要知道两个内容:

  1. redo log(重做日志),是 innodb 存储引擎所独有的。
  2. binlog(操作日志),是mysql服务所拥有的。

例如如下的语句:

begin
update user set name = 'xx' where id = 1;
commit;

上面的语句在执行的过程中分为两个阶段:

  1. 首先将要更改的信息记录在 redo log中,并标记为 prepare.
  2. 当commit的时候,会首先将修改记录在binlog中,然后在 redo log中标记为 commit.

面试, 什么叫做两阶段提交?

两阶段提交是XA协议中用来解决分布式事务问题的一种方式,底层是通过 redo log和binlog来实现的,第一个阶段用户执行sql语句的时候,会将用户的修改记录在 redo log中,标记为prepare状态;当事务提交的时候,会将操作记录在 binlog 中,接着将 redo log 中之前标记的数据改为 commit状态。在具体的业务层实现的时候,需要一个协调者,多个事务的参与者,第一个阶段的时候,协调者会让所有的事务的参与者进入准备阶段,如果事务的参与者都准备成功了,会给协调者发送yes的状态,当协调者收到所有参与者返回的正常状态,就进入第二阶段,如果某一个事务参与者准备失败,协调者会向所有的参与者发送回滚指令;第二阶段,事务的协调者会向所有的参与者发送commit指令提交事务,最后所有的参与者如果事务执行成功,会返回一个ack确认;

两阶段提交有什么问题?

  1. 所有的步骤都是阻塞状态和没有超时机制。
  2. 在第二个阶段的时候,当其中一个事务的参与者事务提交失败,可能会导致事务的不一致性。

3.3 三阶段提交

面试,没接触的。

3.4 TCC

TCC(Try Commit Cancel)应该业界出现比较早的分布式事务解决方案。预检查阶段,提交,取消阶段。

  1. 对业务的侵入性太强,要提供给TCC框架接口,供其调用。
  2. 实现难度特别大。

3.5 SAGA

SAGA是TCC的改进版,少了一个Try阶段。

3.6 Seata(AT事务)

AT模式是阿里 Seata 这个框架提出的分布式事务的概念。

四. Seata的搭建

4.1 数据库的创建

4.1.1 seata端数据库创建

一. 创建一个数据库,名字叫 seata

二. 执行sql语句:https://gitee.com/seata-io/seata/blob/develop/script/server/db/mysql.sql

4.1.2 业务端创建数据

在所有的业务数据库中都要执行SQL语句:https://gitee.com/seata-io/seata/blob/develop/script/client/at/db/mysql.sql

4.2 修改配置

修改 SEATA_HOME/config/registry.conf 文件。

registry {
  # file 、nacos 、eureka、redis、zk、consul、etcd3、sofa
  # 第三行位置,默认值 file, 将其改为 nacos
  type = "nacos"   

  nacos {
    application = "seata-server"
    serverAddr = "127.0.0.1:8848"
    group = "SEATA_GROUP"
    namespace = ""
    cluster = "default"
    username = ""
    password = ""
  }
  eureka {
    serviceUrl = "http://localhost:8761/eureka"
    application = "default"
    weight = "1"
  }
config {
  # file、nacos 、apollo、zk、consul、etcd3
  # 第59行,默认值为 file, 改为 nacos
  type = "nacos"

  nacos {
    serverAddr = "127.0.0.1:8848"
    namespace = ""
    group = "SEATA_GROUP"
    username = ""
    password = ""
    dataId = "seataServer.properties"
  }
  consul {
    serverAddr = "127.0.0.1:8500"
    aclToken = ""
  }

4.3 seata连接数据库

修改SEATA_HOME/config/file.conf 文件。

## transaction log store, only used in seata-server
store {
  ## store mode: file、db、redis
  # 将mode改为db就行了
  mode = "db"
  ## rsa decryption public key
  publicKey = ""
  ## file store property
  file {
## 如下的配置,视情况而定
db {
    ## the implement of javax.sql.DataSource, such as DruidDataSource(druid)/BasicDataSource(dbcp)/HikariDataSource(hikari) etc.
    datasource = "druid"
    ## mysql/oracle/postgresql/h2/oceanbase etc.
    dbType = "mysql"
    driverClassName = "com.mysql.jdbc.Driver"
    ## if using mysql to store the data, recommend add rewriteBatchedStatements=true in jdbc connection param
    url = "jdbc:mysql://127.0.0.1:3306/seata?rewriteBatchedStatements=true"
    user = "root"
    password = "123456"
    minConn = 5
    maxConn = 100
    globalTable = "global_table"
    branchTable = "branch_table"
    lockTable = "lock_table"
    queryLimit = 100
    maxWait = 5000
}

4.4 启动seata

在 window 环境下双击 SEATA_HOME/bin/seata-server.bat

4.5 配置seata的信息

配置信息的地址:https://gitee.com/seata-io/seata/blob/develop/script/config-center/config.txt

将配置信息添加到 nacos 的配置项中。

service.vgroupMapping.my_test_tx_group=default
store.db.datasource=druid
store.db.dbType=mysql
store.db.driverClassName=com.mysql.jdbc.Driver
store.db.url=jdbc:mysql://127.0.0.1:3306/seata?useUnicode=true&rewriteBatchedStatements=true
store.db.user=root
store.db.password=123456
store.db.minConn=5
store.db.maxConn=30
store.db.globalTable=global_table
store.db.branchTable=branch_table
store.db.queryLimit=100
store.db.lockTable=lock_table
store.db.maxWait=5000

请添加图片描述

4.6 项目中使用

4.6.1 引入依赖
<dependency>
      <groupId>com.alibaba.cloud</groupId>
      <artifactId>spring-cloud-starter-alibaba-seata</artifactId>
</dependency>
4.6.2 配置

在所有的事务参与方的配置文件中加上如下的配置:

server:
  port: 8083

spring:
  datasource:
    username: root
    password: 123456
    url: jdbc:mysql://localhost:3306/test?serverTimezone=Asia/Shanghai
    driver-class-name: com.mysql.cj.jdbc.Driver
    hikari:
      # 最小空闲的连接的数量
      minimum-idle: 2
      # 最大的数量, mysql的最大连接数量 1000 左右
      maximum-pool-size: 100
  application:
    name: seata-user
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
        register-enabled: true
        
# 如下都是seata的配置
seata:
  tx-service-group: my_test_tx_group

  # 表示我们的业务寻找seata的地址
  registry:
    type: nacos
    nacos:
      server-addr: 127.0.0.1:8848

  # 我们的业务代码到nacos拉去我们seata的配置
  config:
    type: nacos
    nacos:
      server-addr: 127.0.0.1:8848
      group: DEFAULT_GROUP
4.6.3 配置分布式事务

在事务的发起方加上 @GlobalTransactional(rollbackFor = Exception.class) 就可以了。

4.7 AT模式

AT模式是 Seata 独创的,不是业界通用的事务解决方案。它中间涉及到三方来共同完成 AT 模式:

  1. TC(Transaction Coordinate): 事务的协调者,就是 seata 服务器。
  2. TM(Transaction Manager): 事务的管理者,是在事务的发起方创建的。
  3. RM (Resource Manager): 资源管理者。

说一下AT模式?

​ AT模式Seata框架提出的,它包含三个角色 事务的协调者(Seata的服务器)事务的管理者(存在于事务的发起方)资源管理者(分支事务)。当我们发起一个分布式事务,首先会创建一个 事务的管理者, 会上报给 事务的协调者 ,事务的协调者会下发一个 XID, 当事务的管理者收到 XID, 就会创建一个全局事务,在这个事务的调用链路过程中,会在http请求头中携带 XID,接着每个资源管理者执行分支事务的时候,会向事务的协调者上报分支事务的执行状况,同时会将事务执行之前的数据做一个快照,保存在 undo_log 表中,当分支事务执行之后,会将执行之后的数据与之前数据进行合并,当所有的分支事务执行结束后,那么就由事务的管理者向事务协调者请求提交或者回滚事务。


版权声明:本文为sylvia_lily原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。