更正问题
上篇博客Spring Cloud Alibaba Seata分布式事务使用入门介绍了事务的ACID特性,分布式CAP原则,以及分布式事务常见解决方案。这篇博客做一些在使用阿里的开源分布式组件Seata的一些补充。
本着实事求是的态度,今天上午又巴拉巴拉的翻阅了Seata官网的博客,查看了别人使用nacos作为seata服务端的配置中心,导入的nacos中的结果跟我上篇博客中的一致。这里更正上篇博客中自己误认为nacos导入脚本存在问题导致导入配置错误。
在Seata官网的博客中,也找到了关于file.conf
文件中配置为db模式(file模式适合单机,db模式适合集群)的建表语句:
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;
-- the table to store BranchSession data
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;
-- the table to store lock data
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;
因为Seata本身的更新也比较快,每个人使用版本不一致,文档不一致,建议先阅读官方文档,博客,并查看版本更新日志,自己动手,有问题再解决呗。
本篇博客用来对Seata做一点小小的补充
Seata AT 模式
AT模式案例直接去官网中查阅即可,建表语句,业务核心伪代码都在,自己搭建TC,TM,RM。
这里重新模拟成功,失败的测试用例,查看全方面的日志,进行分析。
模拟成功
-
TC 日志
-
TM 日志
Business-Service 控制台
-
RM 日志
-
Storage-Service控制台
-
Account-Service控制台
-
Order-Service控制台
-
通过上面的日志结合一起来分析:
- 调用Business-Service中的purchase方法,向TC开启全局事务,并生成全局的xid:ip:port:79972829746958336
- Storage-Service,进行更新操作(业务),注册分支(branchId = 79972830313189376),并在对应数据库的
undo_log
中插入数据,这个数据用于更新操作的回滚。最后向TC通知操作结果。 - Account-Service,更新数据(业务),注册分支(branchId = 79972831156244480),
undo_log
表插入数据,通知TC操作结果 - Order-Service,插入数据(业务),注册分支(branchId = 79972831894441984),
ndo_log
表插入数据,通知TC操作结果 - Business-Service执行成功,通知TC全局提交
- TC通知所有RM提交成功,RM会删除掉
undo_log
表中的数据。
模拟失败
在模拟中发现,成功或失败,undo_log
表中的数据被清理的很快,我暂时没有抓取到undo_log
表中的数据。
-
TC 日志
-
TM 日志
Business-Service 控制台
-
RM 日志
-
Storage-Service控制台
-
Account-Service控制台
-
Order-Service控制台
-
通过日志分析,在Account-Service中我手动抛出了异常,Storage-Service中的更新操作,通过undo_log
表中的日志数据,进行了回滚。
通过对undo_log
表字段分析:
xid,branch_id通过日志都看到了。
通过字段类型来看,这里面应该是记录回滚前后的详细数据信息。
好在官网给了一张
官方给的详细的流程是:
- RM收到TC的分支回滚请求后,开启一个本地事务
- 通过xid和branch id查找
undo_log
表中的记录 - 数据校验
- 根据
undo_log
表中的信息生成回滚SQL并执行 - 提交本地事务,并把回滚的结果反馈给TC
写隔离
AT模式下,在一阶段本地事务提交前,必须要拿到全局锁。如果拿不到,则不能提交本地的事务。在不停尝试拿全局锁时,超出了一定范围,则放弃拿全局锁,回滚本地事务,并释放本地锁。
读隔离
在数据库本地事务隔离级别为读已提交(或以上基础上),Seata AT模式默认全局隔离是读未提交。
如果在特定场景下,必须要求全局的读已提交,则通过SELECT ... FRO UPDATE
语句代理。
关于写隔离、读隔离。请阅读官网结合的例子理解。Seata AT模式
Seata XA模式
关于XA模式,与AT模式不同的是在RM中以XA协议的机制来管理分支事务。
在AT模式的代码中改动不是特别多,只需要在数据源配置的类中修改一下返回数据源的具体实现类。
这里说一下重点,因为是自己配置数据源,我们需要将自动配置的类给移除掉(不然启动会报错)。
在启动类的@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
附
Seata TCC模式,Seata Saga模式,我这里直接去GitHub上面拉取的例子运行的。
直接去拉取例子,结合文档吧。比我这讲的老详细了。0.0