Seata(Simple Extensible Autonomous Transaction Architecture) 是阿里巴巴开源的分布式事务中间件,以高效并且对业务零侵入的方式,解决微服务场景下面临的分布式事务问题。

一、分布式事务管理中的 CAP定理

1、CAP 定理(也称为 Brewer 定理),指的是在分布式计算环境下,有3个核心的要求
(1)一致性(Consistency),所有实例节点同一时间看到是相同的数据
(2)可用性(Availability),不管是否成功,确保每一个请求都能接收到响应
(3)分区容错性(Partition Tolerance),系统任意分区后,在网络故障时,仍能操作
2、CAP理论告诉我们,分布式系统不可能同时满足以三种要求

最多只能同时满足其中的两项,大多数分布式业务中P是必须的, 因此往往选择就在CP或者AP。

(1)CA: 放弃分区容错性。非分布式架构,比如关系数据库,因为没有分区,但是在分布式系统下,CA组合就不建议了。

(2)AP: 放弃强一致性。追求最终一致性,类似的场景比如转账,可以接受两小时后到账,Eureka的注册也是类似的做法。

(3)CP: 放弃可用性。zookeeper在leader宕机后,选举期间是不提供服务的。类似的场景比如支付完成之后出订单,必须一进一出都完成才行。
说明:在分布式系统中AP运用的最多,因为它放弃的是强一致性,追求的是最终一致性,性价比最高。

二、分布式事务管理AT模式

分布式事务Seata有三种模式,这里重点介绍一下AT模式。

AT:两阶段提交协议。

TCC:是服务化的两阶段提交协议,业务开发者需要实现这三个服务接口。

第一阶段服务由业务代码编排来调用 Try 接口进行资源预留,所有参与者的 Try 接口都成功了,事务管理器会提交事务,并调用每个参与者的 Confirm 接口真正提交业务操作,否则调用每个参与者的 Cancel 接口回滚事务。

Saga:是一种补偿协议,在 Saga 模式下,分布式事务内有多个参与者,每一个参与者都是一个冲正补偿服务,需要用户根据业务场景实现其正向操作和逆向回滚操作。

1.1 AT模式角色分析

官方原文:

Transaction Coordinator(TC): Maintain status of global and branch transactions, drive the global commit or rollback.
​
Transaction Manager(TM): Define the scope of global transaction: begin a global transaction, commit or rollback a global transaction.
​
Resource Manager(RM): Manage resources that branch transactions working on, talk to TC for registering branch transactions and reporting status of branch transactions, and drive the branch transaction commit or rollback.

原文解释:

1、Transaction Coordinator (TC): 事务协调器,维护全局事务的运行状态,负责协调并驱动全局事务的提交或回滚

2、Transaction Manager (TM) : 控制全局事务的边界,负责开启一个全局事务,并最终发起全局提交或全局回滚的决议

3、Resource Manager (RM): 控制分支事务,负责分支注册、状态汇报,并接收事务协调器的指令,驱动分支(本地)事务的提交和回滚

2.2 AT模式工作流程

1、一阶段提交

注册分支事务,记录undo-log、执行业务sql并提交、报告事务状态;

业务数据和回滚日志记录在同一个本地事务中提交,释放本地锁和连接资源。

2、二阶段提交

如果决议是全局提交,此时分支事务此时已经完成提交,不需要同步协调处理(只需要异步清理回滚日志),Phase2 可以非常快速地完成。

3、二阶段回滚

如果决议是全局回滚,RM 收到协调器发来的回滚请求,通过 XID 和 Branch ID 找到相应的回滚日志记录,通过回滚记录生成反向的更新 SQL 并执行,以完成分支的回滚。

三、Seata应用

3.1 下载seata-server

 官网地址:

https://github.com/apache/incubator-seata/releases/download/v2.0.0/seata-server-2.0.0.zip

解压seata-server-2.0.0.zip文件,将解压后的seata文件夹拷贝到D:\programd下。

3.2 配置seata-sever

1、配置seata-server数据源与数据库

(1)配置seata-server数据源

配置文件file.conf设置(没有就创建一个): D:\programd\seata\conf\file.conf

(2)创建seata数据库

create database seata

(3)创建3张表

创建表global_table:

CREATE TABLE IF NOT EXISTS `global_table`
(
    `xid`                       VARCHAR(128) NOT NULL,
    `transaction_id`            BIGINT,
    `status`                    TINYINT      NOT NULL,
    `application_id`            VARCHAR(32),
    `transaction_service_group` VARCHAR(32),
    `transaction_name`          VARCHAR(128),
    `timeout`                   INT,
    `begin_time`                BIGINT,
    `application_data`          VARCHAR(2000),
    `gmt_create`                DATETIME,
    `gmt_modified`              DATETIME,
    PRIMARY KEY (`xid`),
    KEY `idx_gmt_modified_status` (`gmt_modified`, `status`),
    KEY `idx_transaction_id` (`transaction_id`)
) ENGINE = InnoDB
  DEFAULT CHARSET = utf8;

​创建表branch_table:

CREATE TABLE IF NOT EXISTS `branch_table`
(
    `branch_id`         BIGINT       NOT NULL,
    `xid`               VARCHAR(128) NOT NULL,
    `transaction_id`    BIGINT,
    `resource_group_id` VARCHAR(32),
    `resource_id`       VARCHAR(256),
    `branch_type`       VARCHAR(8),
    `status`            TINYINT,
    `client_id`         VARCHAR(64),
    `application_data`  VARCHAR(2000),
    `gmt_create`        DATETIME(6),
    `gmt_modified`      DATETIME(6),
    PRIMARY KEY (`branch_id`),
    KEY `idx_xid` (`xid`)
) ENGINE = InnoDB
  DEFAULT CHARSET = utf8;

​​创建表lock_table:

CREATE TABLE IF NOT EXISTS `lock_table`
(
   `row_key`        VARCHAR(128) NOT NULL,
   `xid`            VARCHAR(96),
   `transaction_id` BIGINT,
   `branch_id`      BIGINT       NOT NULL,
   `resource_id`    VARCHAR(256),
   `table_name`     VARCHAR(32),
   `pk`             VARCHAR(36),
   `gmt_create`     DATETIME,
   `gmt_modified`   DATETIME,
   PRIMARY KEY (`row_key`),
   KEY `idx_branch_id` (`branch_id`)
) ENGINE = InnoDB
 DEFAULT CHARSET = utf8;

2、修改seata的注册中心

配置文件为(没有就创建一个)D:\programd\seata\conf\registry.conf​​​​​​

3、修改seata的配置中心

配置文件还是D:\programd\seata\conf\registry.conf

4、通过Nacos配置中心管理seata配置

(1)下载配置项:https://github.com/seata/seata/tree/develop/script/config-center

内容如下:

transport.type=TCP
transport.server=NIO
transport.heartbeat=true
transport.enableClientBatchSendRequest=false
transport.threadFactory.bossThreadPrefix=NettyBoss
transport.threadFactory.workerThreadPrefix=NettyServerNIOWorker
transport.threadFactory.serverExecutorThreadPrefix=NettyServerBizHandler
transport.threadFactory.shareBossWorker=false
transport.threadFactory.clientSelectorThreadPrefix=NettyClientSelector
transport.threadFactory.clientSelectorThreadSize=1
transport.threadFactory.clientWorkerThreadPrefix=NettyClientWorkerThread
transport.threadFactory.bossThreadSize=1
transport.threadFactory.workerThreadSize=default
transport.shutdown.wait=3

service.vgroupMapping.fengmi_tx_group=default
service.default.grouplist=127.0.0.1:8091
service.enableDegrade=false
service.disableGlobalTransaction=false

client.rm.asyncCommitBufferLimit=10000
client.rm.lock.retryInterval=10
client.rm.lock.retryTimes=30
client.rm.lock.retryPolicyBranchRollbackOnConflict=true
client.rm.reportRetryCount=5
client.rm.tableMetaCheckEnable=false
client.rm.tableMetaCheckerInterval=60000
client.rm.sqlParserType=druid
client.rm.reportSuccessEnable=false
client.rm.sagaBranchRegisterEnable=false
client.tm.commitRetryCount=5​
client.tm.rollbackRetryCount=5
client.tm.defaultGlobalTransactionTimeout=60000
client.tm.degradeCheck=false
client.tm.degradeCheckAllowTimes=10
client.tm.degradeCheckPeriod=2000

store.mode=db
store.publicKey=
store.file.dir=file_store/data
store.file.maxBranchSessionSize=16384
store.file.maxGlobalSessionSize=512
store.file.fileWriteBufferCacheSize=16384
store.file.flushDiskMode=async
store.file.sessionReloadReadSize=100
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/sdb?useUnicode=true
store.db.user=root
store.db.password=1234
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
store.redis.mode=single
store.redis.single.host=127.0.0.1
store.redis.single.port=6379
store.redis.maxConn=10
store.redis.minConn=1
store.redis.maxTotal=100
store.redis.database=0
store.redis.password=
store.redis.queryLimit=100

server.recovery.committingRetryPeriod=1000
server.recovery.asynCommittingRetryPeriod=1000
server.recovery.rollbackingRetryPeriod=1000
server.recovery.timeoutRetryPeriod=1000
server.maxCommitRetryTimeout=-1
server.maxRollbackRetryTimeout=-1
server.rollbackRetryTimeoutUnlockEnable=false

client.undo.dataValidation=true
client.undo.logSerialization=jackson
client.undo.onlyCareUpdateColumns=true
server.undo.logSaveDays=7
server.undo.logDeletePeriod=86400000
client.undo.logTable=undo_log
client.undo.compress.enable=true
client.undo.compress.type=zip
client.undo.compress.threshold=64k

log.exceptionRate=100
transport.serialization=seata
transport.compressor=none
metrics.enabled=false
metrics.registryType=compact
metrics.exporterList=prometheus
metrics.exporterPrometheusPort=9898

(2)个性相关配置项

service.vgroupMapping.fengmi_tx_group=default
store.mode=db
store.db.driverClassName=com.mysql.jdbc.Driver
store.db.url=jdbc:mysql://127.0.0.1:3306/sdb?useUnicode=true
store.db.user=root
store.db.password=1234

(3)创建配置文件application.properties, 目前只支持properties, 不支持yml格式。

(4)启动seata-server

切换到seata的bin文件夹命令窗口输入:seata-server.bat -h 127.0.0.1 -p 8098

启动后效果如下:

四、Seata客户端

一个调用链中的所有微服务都是seata的客户端,都必须走下面的步骤。

1、创建undo_log表

​CREATE TABLE IF NOT EXISTS `undo_log`​(​`branch_id` BIGINT(20) NOT NULL COMMENT 'branch transaction id',​ 
`xid`  VARCHAR(100) NOT NULL COMMENT 'global transaction id',​
`context` VARCHAR(128) NOT NULL COMMENT 'undo_log context,such as serialization',
`rollback_info` LONGBLOB NOT NULL COMMENT 'rollback info',​
`log_status` INT(11)  NOT NULL COMMENT '0:normal status,1:defense status',
`log_created` DATETIME(6) NOT NULL COMMENT 'create datetime',​
`log_modified`  DATETIME(6)  NOT NULL COMMENT 'modify datetime',​
UNIQUE KEY `ux_undo_log`(`xid`, `branch_id`)​) ENGINE = InnoDB​ AUTO_INCREMENT = 1​ DEFAULT CHARSET = utf8 COMMENT ='AT transaction mode undo table';

2、pom依赖

<!-- seata -->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-alibaba-seata</artifactId>
    <version>2.2.0.RELEASE</version>
    <exclusions>
        <exclusion>
            <groupId>io.seata</groupId>
            <artifactId>seata-spring-boot-starter</artifactId>
        </exclusion>
    </exclusions>
</dependency>
​
<dependency>
    <groupId>io.seata</groupId>
    <artifactId>seata-spring-boot-starter</artifactId>
    <version>1.4.2</version>
</dependency>

3、配置设置

seata:
  enabled: true
  tx-service-group: fengmi_tx_group
  enable-auto-data-source-proxy: true
  config:
    type: nacos
    nacos:
      server-addr: 127.0.0.1:8848
      group: DEFAULT_GROUP
      namespace: sit
      username: nacos
      password: nacos
      data-id: fengmi_tx_group-sit.properties
​
  registry: #发现seata-server
    type: nacos
    nacos:
      application: seata-server
      server-addr: 127.0.0.1:8848
      namespace: sit
      group: DEFAULT_GROUP
      username: nacos
      password: nacos

4、在Service与Controller类中的方法上添加注解@Globaltransational

五、Seata全局事务并发隔离

1、读隔离

一个全局事务修改数据,另外一个全局读取数据怎么解决脏读。

在数据库本地事务隔离级别读已提交(Read Committed)或以上的基础上,Seata(AT模式)的默认全局隔离级别是读未提交(Read Uncommitted)。

如果应用在特定场景下,必需要求全局的读已提交 ,目前 Seata 的方式是通过 SELECT  FOR UPDATE语句的代理。

2、写隔离

一阶段本地事务提交前,需要确保先拿到 全局锁

拿不到 全局锁 ,不能提交本地事务。

更多精彩内容请关注本站!!!

Logo

更多推荐