总述

低代码平台接入的分布式事务管理组件为seata 1.4,具体参考seata

环境管理

dev/test:http://nacos-dev.gz.cvte.cn
uat: https://nacos-uat.gz.cvte.cn
pro: https://nacos.gz.cvte.cn

客户端接入官方快速开始

  1. MAVEN依赖引入
<dependency>
    <groupId>io.seata</groupId>
    <artifactId>seata-spring-boot-starter</artifactId>
    <version>1.4.2</version>
</dependency>
 <dependency>
     <groupId>com.alibaba.nacos</groupId>
     <artifactId>nacos-client</artifactId>
     <version>1.4.0</version>
 </dependency>
  1. 创建undo表
  • PostgreSQL
-- auto-generated definition
create table undo_log
(
    id            serial
        constraint pk_undo_log
            primary key,
    branch_id     bigint       not null,
    xid           varchar(100) not null,
    context       varchar(128) not null,
    rollback_info bytea        not null,
    log_status    integer      not null,
    log_created   timestamp(0) not null,
    log_modified  timestamp(0) not null,
    constraint ux_undo_log
        unique (xid, branch_id)
);
  • Mysql
CREATE TABLE `undo_log` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `branch_id` bigint(20) NOT NULL,
  `xid` varchar(100) NOT NULL,
  `context` varchar(128) NOT NULL,
  `rollback_info` longblob NOT NULL,
  `log_status` int(11) NOT NULL,
  `log_created` datetime NOT NULL,
  `log_modified` datetime NOT NULL,
  `ext` varchar(100) DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `ux_undo_log` (`xid`,`branch_id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
  • Oracle
CREATE TABLE UNDO_LOG(
  ID NUMBER(20) ,
  BRANCH_ID NUMBER(20) NOT NULL,
  XID VARCHAR2(100) NOT NULL,
  context VARCHAR2(256) NOT NULL,
  LOG_STATUS NUMBER(11) NOT NULL,
  rollback_info BLOB NOT null ,
  LOG_CREATED DATE NOT NULL,
  LOG_MODIFIED DATE NOT NULL,
  EXT VARCHAR2(100) DEFAULT NULL,
  CONSTRAINT PK_UNDO_LOG PRIMARY KEY (ID)
);

create sequence UNDO_LOG_SEQ
  minvalue 1
  maxvalue 9999999999
  start with 1
  increment by 1
  cache 20;
  1. 配置管理Client端官方说明
  • 代码中yml或properties文件注入

    seata.application-id=core-application # 服务应用名
    seata.tx-service-group=core_application_tx_group # 事务组
    seata.service.vgroup-mapping.core_application_tx_group=default 
  • apollo配置

seata.enabled = true

seata.registry.type = nacos
seata.registry.nacos.application = seata-server
seata.registry.nacos.group = SEATA_GROUP
seata.registry.nacos.server-addr = http://nacos-dev.gz.cvte.cn
seata.registry.nacos.namespace = 1fb3bcc2-03aa-4437-8586-2994b6badea9
seata.registry.nacos.cluster = default
seata.registry.nacos.username = nacos-test
seata.registry.nacos.password = mjG8J715th

seata.enable-auto-data-source-proxy = true
seata.data-source-proxy-mode = AT
seata.use-jdk-proxy = false
seata.client.rm.async-commit-buffer-limit = 10000
seata.client.rm.report-retry-count = 5
seata.client.rm.table-meta-check-enable = false
seata.client.rm.report-success-enable = false
seata.client.rm.saga-branch-register-enable = false
seata.client.rm.saga-json-parser = fastjson
seata.client.rm.lock.retry-interval = 100
seata.client.rm.lock.retry-times = 30
seata.client.rm.lock.retry-policy-branch-rollback-on-conflict = false
client.rm.table.meta.check.enable = false
seata.client.tm.commit-retry-count = 10
seata.client.tm.rollback-retry-count = 10
seata.client.tm.default-global-transaction-timeout = 3600000
seata.client.tm.degrade-check = false
seata.client.tm.degrade-check-allow-times = 10
seata.client.tm.degrade-check-period = 2000
seata.client.undo.data-validation = true
seata.client.undo.log-serialization = jackson
seata.client.undo.only-care-update-columns = true
seata.client.undo.log-table = undo_log
seata.log.exception-rate = 100
seata.service.enable-degrade = false
seata.service.disable-global-transaction = false
seata.transport.shutdown.wait = 3
seata.transport.thread-factory.boss-thread-prefix = NettyBoss
seata.transport.thread-factory.worker-thread-prefix = NettyServerNIOWorker
seata.transport.thread-factory.server-executor-thread-prefix = NettyServerBizHandler
seata.transport.thread-factory.share-boss-worker = false
seata.transport.thread-factory.client-selector-thread-prefix = NettyClientSelector
seata.transport.thread-factory.client-selector-thread-size = 3
seata.transport.thread-factory.client-worker-thread-prefix = NettyClientWorkerThread
seata.transport.thread-factory.worker-thread-size = default
seata.transport.thread-factory.boss-thread-size = 3
seata.transport.type = TCP
seata.transport.server = NIO
seata.transport.heartbeat = true
seata.transport.serialization = seata
seata.transport.compressor = none
seata.transport.enable-client-batch-send-request = true
logging.level.io.seata = DEBUG
  1. 业务代码

在开发业务的最开始的servicemethod上加入该注解即可开启分布式事务

@GlobalTransactional 

高级延伸

1. 拦截器和过滤器配置

过滤器:用来获取调用方传入的全局事务ID,并绑定在RootContext上下文中,如示例中的类:SeataFilter
拦截器:用来拦截发出的rest请求,并将上下文中的全局事务ID传入到请求头中,如示例中的类:SeataRestTemplateInterceptor,通过 SeataRestTemplateAutoConfiguration注入到RestTemplateConfig的bean中。
注意:restTemplate可以替换底层实现为HttpClient(构造方法传入特定的RequestFactory等方式),参考:https://www.cnblogs.com/yourbatman/p/11532777.html

2. seata的事务管理

只需在聚合服务上用注解@GlobalTransactional开启全局事务,聚合服务编排调用其他事务分支的接口。seata事务的读写隔离,都可以通过全局锁来保证。

本地锁
本地事务获取数据库数据的锁,例如id=1的记录。
全局锁
为保证其他事务在当前事务还未释放全局锁时无法修改同一数值,实现了写的隔离.整个过程 全局 锁 在当前全局事务结束前一直是被全局事物持有的,其实就是seata服务器对“数据库地址+schema+表名+主键值”等维度组成的唯一key进行上锁。

分支注册流程
每个资源都有一个全局唯一的资源 XID,并且在初始化时用该 XID 向 TC 注册资源,当前事务会根据这个XID创建出一个创建了一个GlobalSession然后进行持久化存储在服务端,默认是采用File的方式存储(当然也可以选择db的方式等),随后在第一阶段结束提交前会向GlobalSession注册分支(注册一个以目标数值的键值为键的全局锁),如果全局锁已存在,则进行不停重试,直到注册成功.

上下文如何传递
Seata 的事务上下文由 RootContext 来管理。
应用开启一个全局事务后,RootContext 会自动绑定该事务的 XID,事务结束(提交或回滚完成),RootContext 会自动解绑 XID。

逆向sql的生成条件
seata是根据主键生成逆向sql的,故而要生成逆向sql必须让seata能够从正向sql中拿到主键。

写隔离
一阶段本地事务提交前,需要确保先拿到数据的全局锁 ,否则不能提交本地事务;提交本地事务后才 释放全局锁,故多个全局事务之间的本地事务是写隔离的。

读隔离
在数据库本地事务隔离级别 读已提交(Read Committed) 或以上的基础上,Seata(AT 模式)的默认全局隔离级别是 读未提交(Read Uncommitted)。如果应用在特定场景下,必需要求全局的 读已提交 ,目前 Seata 是可以通过select for update语句的代理,会申请 全局锁。

select for update语句代理执行器处理逻辑如下图:
1、先查询出数据结果,再获取构造数据全局锁的key:表名+主键值(支持批量)
2、判断是否在seata的全局事务或全局锁下,否则报错(不支持select for udpate语句);是就向TC申请数据的全局锁。

3、获取全局锁(就是给数据上锁):传入全局事务id、上锁的数据记录key、数据库资源(地址+schema);
上锁成功,则继续往下处理,待当前全局事务结束后GlobalSession才释放的全局锁。
获取全局锁失败则会抛出LockConflictException异常。

注意:上述的seata事务机制必须在开启了全局事务@GlobalTransaction,或开启了全局锁@GlobalLock的上下文环境下才能成功。

3. 关于spring resttemplate超时设置

TC的全局事务id好像是默认1分钟触发分支事务超时回滚,要配置session-timeout和connect-timeout。
rest接口超时配置,参考相关底层HttpURLConnection的配置:https://www.cnblogs.com/coolsoul/p/11785021.html
如果是使用Ribbon,则在配置文件中设置 http建立socket超时时间和http读取响应socket超时时间

#Ribbon配置
ribbon:
# http建立socket超时时间,毫秒
    ConnectTimeout: 6000
# http读取响应socket超时时间
    ReadTimeout: 12000

Q&A

1.表或视图不存在

场景现象 解决方案
数据库插入、更新、删除操作时候,数据库表明明存在还是报这个错 很有可能是undo_log表没有建
日志显示连接的数据库不对 切换或者配置数据库不对,或者存在缓存
作者:聂维  创建时间:2022-11-25 15:13
最后编辑:聂维  更新时间:2024-12-11 10:09