总述
低代码平台接入的分布式事务管理组件为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
客户端接入官方快速开始
- 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>
- 创建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;
- 配置管理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
- 业务代码
在开发业务的最开始的service
的method
上加入该注解即可开启分布式事务
@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表没有建 |
日志显示连接的数据库不对 | 切换或者配置数据库不对,或者存在缓存 |
最后编辑:聂维 更新时间:2024-12-11 10:09