• 博客园logo
  • 会员
  • 周边
  • 众包
  • 新闻
  • 博问
  • 闪存
  • 赞助商
  • Chat2DB
    • 搜索
      所有博客
    • 搜索
      当前博客
  • 写随笔 我的博客 短消息 简洁模式
    用户头像
    我的博客 我的园子 账号设置 会员中心 简洁模式 ... 退出登录
    注册 登录
打工人丶
博客园    首页    新随笔    联系   管理    订阅  订阅

SpringCloud Alibaba-10-分布式事务

1. 什么是分布式事务

1.1 事务

严格意义上的事务实现应该是具备原子性、一致性、隔离性和持久性,简称 ACID。通俗意义上来说,事务就是为了使得一些更新等操作要么都成功,要么都失败。

  • 原子性(Atomicity):可以理解为一个事务内的所有操作要么都执行,要么都不执行。

  • 一致性(Consistency):在一个事务执行之前和执行之后数据库都必须处于一致性状态,比如你账上有400,我账上有100,你给我打200块,此时你账上的钱应该是200,我
                     账上的钱应该是300,不会存在我账上钱加了,你账上钱没扣的中间状态。

  • 隔离性(Isolation):指的是多个事务并发执行的时候不会互相干扰,即一个事务内部的数据对于其他事务来说是隔离的。

  • 持久性(Durability):指的是一个事务完成了之后数据就被永远保存下来,之后的其他操作或故障都不会对事务的结果产生影响。

其中,原子性和持久性就是靠undo和redo日志来实现的。在Mysql中,有许多日志文件,这2个文件就是与事务有关的。

1.2 undo日志

undo日志:用于保证事务的原子性。
原理:

  1. 在操作任何数据之前,先将数据备份到Undo Log。
  2. 然后进行数据的修改。
  3. 若出现了错误或用户执行了ROLLBACK语句,系统就可以利用Undo Log中的备份数据恢复到事务开始之前的状态。

流程举例:

  1. 事务开始
  2. 记录A=1到undo log
  3. 修改A=3
  4. 记录B=2到undo log
  5. 修改B=4
  6. 将undo log写到磁盘
  7. 将数据写到磁盘
  8. 事务提交

1.3 redo日志

redo日志:用于保证事务的持久性
原理:

  1. redo log与undo log 相反,redo log记录的是新数据的备份,undo log记录的是旧数据的备份
  2. 在事务提交前只需要将redo log持久化即可。

流程举例:

  1. 事务开始
  2. 记录A=1到undo log
  3. 修改A=3
  4. 记录A=3到redo log
  5. 记录B=2到undo log
  6. 修改B=4
  7. 记录B=4到redo log
  8. 将undo log写到磁盘
  9. 将redo log写入磁盘
  10. 事务提交

1.4 分布式事务

分布式事务:顾名思义就是要在分布式系统中实现事务,它其实是由多个本地事务组合而成。
会产生分布式事务的情况:

  • 跨服务的分布式事务

  • 跨数据源的分布式事务 【当数据库单表一年产生的数据超过1000W,就考虑分库分表,如果一个操作既要访问01库,又要访问02库,且要保证数据的一致性,就要用分布式事务】

  • 综合情况

什么是跨数据源
随着业务数据规模的快速发展,数据量越来越大,单库单表已经到瓶颈,不能满足我们的需求。此时我们就会对数据库进行水平拆分,将原本的单库单表拆分成多库多表。所以就出现了跨数据源事务问题。
分库分表的入门理解:https://www.cnblogs.com/itlihao/p/14803807.html


什么是跨服务
在分布式项目里面,我们都是拆分了服务的,各个服务在不同的JVM里面,各个JVM里面都有自己的Spring管理的事务。所以就出现了跨服务的事务问题。




2. 分布式理论

前言:为什么要讲分布式理论? 因为:分布式事务的解决就是基于这些理论思想而实现的,一个分布式事务具有CAP三个指标。

2.1 CAP理论


CAP理论:由1998年,加州大学计算机科学家提出,他指出分布式系统有三个指标,我们在设计分布式系统的时候就要考虑这些指标,尽量避免这些问题。

  • C:强一致性,多服务节点情况下,要保证多个服务的数据是一样的。
  • A:可用性,节点们只要收到请求,要保证立马能处理并给回响应结果。
  • P:分区容错性,分区后,可能导致各个区之间因为网络波动问题而无法通信。分区:分布式系统都分布在多个子网络,每个子网络这里就叫它为一个区。比如一台服务在北京,另一台服务在上海。


2.1.1 P:分区容错性

分区:大多数分布式系统都分布在多个子网络,每个子网络这里就叫它为一个区。比如一台服务在北京,另一台服务在上海。

分区容错性:就是指系统进行分区后,可能导致各个区之间因为网络波动问题而无法通信。

注意:只要是分布式系统,分区容错性是不可避免会遇到的问题。 因此可以认为 CAP 的 P 总是成立。


2.1.2 C:一致性

一致性:多服务节点情况下,要保证多个服务的数据是一样的。

  • 强一致性,要求更新过的数据能被后续的访问都能看到

  • 弱一致性,能容忍后续的部分或者全部访问不到

  • 最终一致性,能容忍经过一段时间后,被要求能访问到更新后的数据

数据不一致:因为在分布式系统里面,因为我们会部署多服务集群,所以就会出现多服务数据不一致的问题。

举例:
    1. 有多台服务节点,他们数据现在是一致的。比如定义了 a = 1;
    2. 用户现在要修改a的值,由于负载均衡,他被带来到服务节点1,修改了a = 2;
    3. 用户现在要看a的最新值,由于负载均衡,此时他被带来到服务节点2,得出a的值为1;

    这就是数据不一致的问题。

如何解决不一致:

当用户在服务节点1修改a = 2时,它发一条消息给其他节点,让其他节点也跟它一样将a改为2。如此一来,就能保证数据的一致性。

2.1.3 A:可用性

这里的可用性是指:只要接收到用户请求,就立马告诉用户结果。



回顾上述的一致性,当我们在修改a=2时,他会发送一条信息给其他节点,那么在发送信息给其他节点这段时间或其他节点收到消息还没改a的值时,其他节点恰好接收到了请求,怎么办?

所以,就出现了矛盾:

    如果要满足一致性【让其他节点在更新a值的时候节点暂时不提供服务】,那么就不能满足可用性。

    如果满足可用性【不管其他节点更没更新a的值,只要用户一发请求,就把当前值告诉用户】,那么就不能满足一致性。


2.1.4 如何取舍CAP

什么时候满足CP?
       对一致性要求高的场景。例如:Zookeper
什么时候满足AP?
       对高可用要求较高的场景。例如:Eureka



2.2 BASE理论

BASE是三个单词的缩写:

  • B 基本可用
  • S 软状态
  • E 最终一致性

BASE 理论:是对 CAP 中一致性和可用性权衡的结果,是CAP 理论中AP 方案的延伸。其来源于对大型互联网分布式 实践 的总结,是基于 CAP 定理逐步演化而来的。

BASE理论核心思想是:

    如果无法做到强一致性,那么运行每个应用可以根据自身的业务特点,采用适当的方式来使系统达到最终一致性。 

    如果无法做到高可用,那么运行每个应用可以根据自身的业务特点,采用适当的方式来使系统达到基本可用。 

2.2.1 最终一致性

把告诉其他节点所耗费的那段时间尽可能减少,减少到可接收范围。
在没将其他节点同步数据完成前,其他节点还是提供服务,虽然那段时间来的请求可能得到数据不一致的情况,但是最终能保证消息一致性。


2.2.2 基本可用

因为告诉其他节点所耗费的那段时间尽可能减少了,所以只有那一点点时间其他节点不可用而已,所以就称为基本可用。


2.2.3 软状态

允许系统中的数据存在中间状态,并认为该状态不影响系统的整体可用性,即允许系统在多个不同节点的数据副本存在数据延时。


强一致性:系统写入了什么,读出来的就是什么。
弱一致性:不一定可以读取到最新写入的值,也不保证多少时间之后读取到的数据是最新的,只是会尽量保证某个时刻达到数据一致的状态。
最终一致性:弱一致性的升级版,系统会保证在一定时间内达到数据一致的状态。


2.2.4 BASE理论与CAP理论总结
  1. CAP 是分布式系统设计理论,BASE 是 CAP 理论中 AP 方案的延伸。

  2. CAP理论严格来讲不是三选二,而是CP、AP二选一,因为通常P(分区容错性)是必须得到保证的。

  3. BASE理论面向的是大型高可用、可扩展的分布式系统。与传统ACID特性相反,不是强一致性模型,BASE提出通过牺牲强一致性来获得可用性,并允许数据一段时间内的不一致,但是最终需要达到一致状态。




3. 分布式事务的解决方案

基于分布式事务理论思想,衍生出了很多的分布式事务解决方案:

  • 基于XA协议的两段提交
  • TCC
  • 可靠消息最终一致性
  • AT模式

DTP:1994年由X/OPEN组织定义的用于处理分布式事务的DTP模型,该模型由如下几个角色

应用程序(AP)          我们的微服务
事务管理器(TM)        全局事务管理者
资源管理器(RM)        数据库
通信资源管理器(CRM)    TM与RM之间的通信中间件,用于通知各个本地事务


实现思路:
    在该模型中,一个分布式事务将被拆分成多个本地事务,运行在不同的AP和RM上。
    一个分布式事务的成功实现,要求拆分成的所有本地事务全实现成功。
        因为在本地上,所以本地事务的ACID就能很好解决与实现。
        但是分布式事务是要求所有本地事务同时执行成功,若有一个失败,则全部回滚。

    但是各个本地事务间如何知道其他事务是否执行完毕呢?
        因此,需要一个CRM来通知各个本地事务,事务的状态。
        想让CRM来做这个事,那么你就得按照CRM的要求来,按照人家的规范来开启你的本地事务。不然你A服务你搞个mysql,B服务你搞个Oracle,大家没有统一的规范,CRM就帮我们实现不了这个事。

     XA:就是CRM与TM之间联系的接口规范。定义了用于 通知事务开始,提交,终止,回滚等接口。各个数据库厂商都必须实现这些接口。

3.1 基于XA的两阶段提交2PC方案【基于CAP理论】

基于XA协议的两段提交:就是基于DTP这种思想衍生出来的,将全局事务拆分成两个阶段执行。整个过程中,需要一个协调者,用于协调本地事物。

  • 阶段一:准备阶段,各个本地事务完成本地事务的准备工作。【去执行一次,但不真正提交,将能否成功执行本地事务的结果反馈给协调者(TC)】
  • 阶段二:执行阶段,根据上一阶段执行结果,通知各个本地事务进行提交还是回滚。


缺点:

  • 单点故障问题:第一阶段成功,此时协调者挂了,还没告诉二阶段该怎么做。
  • 数据阻塞问题:各个本地事务开启的时候,都要对数据加锁。此时不能对数据进行操作,包括查询操作。若某个本地事务先执行完,还得等其他事务执行完毕,得到最终结果看是否全部本地事务执行完毕,待协调者第二阶段的处理。

3.2 TCC方案【基于BASE理论】

TCC:相当于是两阶段的一个升级,它还是用的两段提交。但是可以解决基于AX的两阶段提交资源锁定和阻塞的问题。他就像Base理论。两阶段提交那种做法,要求的是强一致性。TCC呢,它达到的效果是最终一致性。

TCC是三个单词的缩写:

  • Try:资源的检测和预留
  • Confirm:执行的业务的提交
  • Cancel:补偿

执行阶段:

  1. 准备阶段(try):资源的检测和预留
  2. 执行阶段(confirm/cancel):根据上一步的结果,判断下面执行的方法,若上一步都成功,则confirm。反之,则concel。

实现原理:
       我们以下单业务中的扣减余额为例,来看下面3个方法的编写。假设账户A原本余额为100,需要余额扣减30元。


阶段一:余额检查,并冻结部分用户金额,此阶段执行完毕,事务已经提交。

  1. 检查用户余额是否充足,若充足,再冻结部分金额(要扣减的金额)
  2. 在账户表中新增冻结金额字段,值为30,余额不变100。此时查询来了余额应该告诉他是70。

阶段二:根据第一阶段的执行情况,执行二阶段的提交或回滚。

  • 提交(confirm):真正的扣款,把冻结金额从余额里面扣除,冻结金额清空【将冻结金额值改为0,将余额改为70】
  • 补偿(cancel):释放之前冻结的余额【真正的余额不变,将冻结金额的值改为0】

优点与缺点:

  • 优点:
  1. TCC模式执行的每个阶段都会提交事务并释放资源,不需要等待其他事务的执行结果。
  2. 若其他事务执行失败,不需要回滚事务,而是执行补偿操作。如此一来,避免了锁资源与等待其他事务时的时间消耗。
  • 缺点:
  1. 代码侵入太严重,需要人为去实现各个阶段,如:一阶段的try功能,二阶段的confirm与cancel功能。
  2. 开发成本高,本来一个业务现在要去编写这么多复杂业务。
  3. 还要考虑安全问题,万一补偿的时候发生异常失败了呢。

3.3 可靠消息最终一致性【BASE理论】

它是利用消息中间件来实现的,本质还是将一个分布式事务拆分成多个本地事务。

基本原理:

  1. 当事务发起者A服务开启事务,先执行本地事务,如转账案例,本地事务先减100。
  2. 然后通过MQ发送消息,B服务接收到消息,B服务加100。

看基本原理,看着挺简单的。需要注意以下几点:

  • 事务发起者A必须确保本地事务成功后,消息一定发送成功
  • MQ必须保证消息正确投递和持久化
  • 事务参与者B必须确保消息最终一定能消费,如果失败要多次重试

总之,就是要保证MQ的消息可靠性!!!!

缺点:

  • 代码侵入较高
  • 依赖于MQ的高可靠性

3.4 AT模式

2019年,Seata开源了一种解决分布式事务的模式,该模式就是AT模式。Seata框架就是基于AT模式这种思想做出来的用于解决分布式事务的组件。

AT模式:是一种无侵入的分布式事务解决方案,可以看作是对TCC模型的一种优化,将TCC模式第二阶段那些操作由Seata框架帮我们实现,解决了TCC代码侵入,编码复杂等问题。



Seata中的几个基本概念:

  • TC - 事务协调者
    维护全局事务,分支事务的状态,决定全局事务的提交或回滚。需要独立部署

  • TM - 事务管理器
    定义全局事务的范围:开始全局事务,提交或回滚全局事务。

  • RM - 资源管理器
    管理分支事务处理的资源,与TC交谈以注册分支事务和报告分支事务的状态,并驱动分支事务提交或回滚。


Seata一阶段原理:

  1. TM事务发起者发起事务时,由于方法上具有@GlobalTransacational注解,该TM会向TC发起全局事务,生成唯一的XID。
  2. RM事务参与者写表,undo_log记录当前数据,操作完成之后,通知TC事务协调者操作结果
  3. 在写表之前,Seata会拦截SQL,解析SQL的语义,找到该SQL要更新的业务数据,在该数据被更新前,将其保存成before image,然后才执行SQL更新数据。
  4. 在写表之后,再将其保存成after image,最后生成行锁(为了防止其他事务再来操作该条数据)。

下图中:log表就是用来保存快照的,lock表就是用来保存行锁的。


Seata二阶段原理:

  1. 如果一阶段各个事务都没问题,那么二阶段就会将一阶段保存的快照数据删除,行锁也删除。

  2. 如果一阶段有问题,那么二阶段就需要回滚一阶段已经执行的SQL。

    先判断当前数据库业务数据和after image数据是否一致。

        若一致,就说明没有脏写。就使用before image还原业务数据,还原数据后自动删除快照和行锁。

        若不一致,就说明有脏写。就需要转人工处理。

        【比如:我要扣款,原本100元,执行事务减去20,现在还剩80。结果事务出错了,Seata帮我建了一个after image,里面存的是我还剩80,现在事务执行失败,我和Seata里面存的结果都是一样80,那么Seata就能利用before image帮我回滚】






4. SpringBoot集成Seata【注册中心和配置中心采用nacos,基于最常用的AT模式】


请参考大佬原文:https://www.it235.com/高级框架/SpringCloudAlibaba/seata.html#编写at模式代码
大佬B站视频(我就是跟着他学的):https://www.bilibili.com/video/BV1BK411K792?p=15
还参考了B站视频:https://www.bilibili.com/video/BV1n7411R7WQ?p=13
seata中文官网:http://seata.io/zh-cn/index.html


4.0 先去下载seata

Seata中的几个基本概念:

  • TC - 事务协调者
    维护全局事务,分支事务的状态,决定全局事务的提交或回滚。需要独立部署

  • TM - 事务管理器
    定义全局事务的范围:开始全局事务,提交或回滚全局事务。

  • RM - 资源管理器
    管理分支事务处理的资源,与TC交谈以注册分支事务和报告分支事务的状态,并驱动分支事务提交或回滚。

现在我们就去官网搞一个TC-事务协调者,下载下来解压。https://github.com/seata/seata/releases


4.1 修改file.conf,将分布式事务相关数据保存到数据库


4.2 去把TC(事务协调者)要用的表建起来

先去建个数据库,名字可以随便取,但是最好叫seata。因为后面要该我们的配置,默认它就叫seata,你不叫seata的话,后面配置的时候就得跟着改。

-- the table to store GlobalSession data
drop table if exists `global_table`;
create table `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`)
);

-- the table to store BranchSession data
drop table if exists `branch_table`;
create table `branch_table` (
  `branch_id` bigint not null,
  `xid` varchar(128) not null,
  `transaction_id` bigint ,
  `resource_group_id` varchar(32),
  `resource_id` varchar(256) ,
  `lock_key` varchar(128) ,
  `branch_type` varchar(8) ,
  `status` tinyint,
  `client_id` varchar(64),
  `application_data` varchar(2000),
  `gmt_create` datetime,
  `gmt_modified` datetime,
  primary key (`branch_id`),
  key `idx_xid` (`xid`)
);

-- the table to store lock data
drop table if exists `lock_table`;
create table `lock_table` (
  `row_key` varchar(128) not null,
  `xid` varchar(96),
  `transaction_id` long ,
  `branch_id` long,
  `resource_id` varchar(256) ,
  `table_name` varchar(32) ,
  `pk` varchar(36) ,
  `gmt_create` datetime ,
  `gmt_modified` datetime,
  primary key(`row_key`)
);

-- the table to store seata xid data
-- 0.7.0+ add context
-- you must to init this sql for you business databese. the seata server not need it.
-- 此脚本必须初始化在你当前的业务数据库中,用于AT 模式XID记录。与server端无关(注:业务数据库)
-- 注意此处0.3.0+ 增加唯一索引 ux_undo_log
drop table `undo_log`;
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;

4.3 修改registry.conf,指定nacos为我们的注册中心


4.4 修改registry.conf,指定nacos为我们的配置中心


4.5 将nacos的配置文件批量推到nacos配置中心

前言:我们已经指定了配置中心,那么就需要把nacos的配置文件放到配置中心去。

   但是因为nacos的配置文件贼多,nacos为我们提供了一个工具,方便我们快速的把配置文件批量推到nacos配置中心去。


    步骤如下:
        1. 下载config.txt。它是一个脚本文件,我们需要执行它,实现快速将配置文件批量推到nacos配置中心去。https://github.com/seata/seata/tree/develop/script/config-center
        
        2. 下载nacos-config.sh,用于执行推送脚本。https://github.com/seata/seata/tree/develop/script/config-center/nacos
  
        3. 修改config.txt
                    1. 修改store.mode 值为db
                    2. 根据自己的数据库配置修改 store.db.xxxx 这块的配置
                    3. 修改 service.vgroupMapping.default_tx_group=default 这块内容
                          语法为: service.vgroup_mapping.${your-service-gruop}=default ,中间的${your-service-gruop} 为自己定义的服务组名称, 这个名称到时候配置文件.yml中用得到。

        4. 执行nacos-config.sh。
              # -h 主机
              # -p 端口号
              # -t 命名空间ID
              # -u 用户名
              # -p 密码
              $ sh nacos-config.sh -h localhost -p 8848 -g SEATA_GROUP -u nacos -w nacos

        # 当你运行成功后,你会发现你的nacos配置列表多了很多seata的配置文件,他们属于SEATA_GROUP配置组。

4.6 开发我们的RM-事务参与者

案例需求:下单减库存。

4.6.1 开发我们的RM-事务参与者-下订单服务
-- 库存服务DB执行
CREATE TABLE `tab_storage` (
  `id` bigint(11) NOT NULL AUTO_INCREMENT,
  `product_id` bigint(11) DEFAULT NULL COMMENT '产品id',
  `total` int(11) DEFAULT NULL COMMENT '总库存',
  `used` int(11) DEFAULT NULL COMMENT '已用库存',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
INSERT INTO `tab_storage` (`product_id`, `total`,`used`)VALUES ('1', '96', '4');
INSERT INTO `tab_storage` (`product_id`, `total`,`used`)VALUES ('2', '100','0');

-- 订单服务DB执行
CREATE TABLE `tab_order` (
  `id` bigint(11) NOT NULL AUTO_INCREMENT,
  `user_id` bigint(11) DEFAULT NULL COMMENT '用户id',
  `product_id` bigint(11) DEFAULT NULL COMMENT '产品id',
  `count` int(11) DEFAULT NULL COMMENT '数量',
  `money` decimal(11,0) DEFAULT NULL COMMENT '金额',
  `status` int(1) DEFAULT NULL COMMENT '订单状态:0:创建中;1:已完成',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
server:
  port: 6770
spring:
  application:
    name: order-service
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    username: root
    password: root
    url: jdbc:mysql://localhost:3306/seata_tb_order?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf8&rewriteBatchedStatements=true
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
        register-enabled: true
      config:
        server-addr: 127.0.0.1:8848
        enabled: true
        file-extension: yaml

seata:
  enabled: true
  application-id: ${spring.application.name}
  # 事务群组(可以每个应用独立取名,也可以使用相同的名字),要与服务端nacos-config.txt中service.vgroup_mapping的后缀对应
  tx-service-group: my_test_tx_group
  config:
    type: nacos
    # 需要和server在同一个注册中心下
    nacos:
      serverAddr: 127.0.0.1:8848
      # 需要server端(registry和config)、nacos配置client端(registry和config)保持一致
      group: SEATA_GROUP
      username: nacos
      password: nacos
  registry:
    type: nacos
    nacos:
      # 需要和server端保持一致,即server在nacos中的名称,默认为seata-server
      application: seata-server
      server-addr: 127.0.0.1:8848
      group: SEATA_GROUP
      username: nacos
      password: nacos
mybatis:
  mapperLocations: classpath:mapper/*.xml
4.6.2 开发我们的RM-事务参与者-减库存服务
server:
  port: 6780
spring:
  application:
    name: storage-service
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    username: root
    password: root
    url: jdbc:mysql://localhost:3306/seata_tb_storage?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf8&rewriteBatchedStatements=true
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848
        register-enabled: true
      config:
        server-addr: localhost:8848
        enabled: true
        file-extension: yaml

seata:
  enabled: true
  application-id: ${spring.application.name}
  # 事务群组(可以每个应用独立取名,也可以使用相同的名字),要与服务端nacos-config.txt中service.vgroup_mapping的后缀对应
  tx-service-group: my_test_tx_group
  config:
    type: nacos
    # 需要和server在同一个注册中心下
    nacos:
      serverAddr: localhost:8848
      # 需要server端(registry和config)、nacos配置client端(registry和config)保持一致
      group: SEATA_GROUP
      username: nacos
      password: nacos
  registry:
    type: nacos
    nacos:
      # 需要和server端保持一致,即server在nacos中的名称,默认为seata-server
      application: seata-server
      server-addr: localhost:8848
      group: SEATA_GROUP
      username: nacos
      password: nacos

mybatis:
  mapperLocations: classpath:mapper/*.xml
4.6.2 开发我们的TM-事务发起者-调用下订单减库存服务
server:
  port: 6760
spring:
  application:
    name: business-service
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
        register-enabled: true
      config:
        server-addr: 127.0.0.1:8848
        enabled: true
        file-extension: yaml

seata:
  enabled: true
  application-id: ${spring.application.name}
  # 事务群组(可以每个应用独立取名,也可以使用相同的名字),要与服务端nacos-config.txt中service.vgroup_mapping的后缀对应
  tx-service-group: my_test_tx_group
  config:
    type: nacos
    # 需要和server在同一个注册中心下
    nacos:
      serverAddr: 127.0.0.1:8848
      # 需要server端(registry和config)、nacos配置client端(registry和config)保持一致
      group: SEATA_GROUP
      username: nacos
      password: nacos
  registry:
    type: nacos
    nacos:
      # 需要和server端保持一致,即server在nacos中的名称,默认为seata-server
      application: seata-server
      server-addr: 127.0.0.1:8848
      group: SEATA_GROUP
      username: nacos
      password: nacos

mybatis:
  mapperLocations: classpath:mapper/*.xml

# feign组件超时设置
feign:
  client:
    config:
      default:
        connect-timeout: 30000
        read-timeout: 30000
    @Autowired
    private OrderClient orderClient;
    @Autowired
    private StorageClient storageClient;

    @GetMapping("buy")
    @GlobalTransactional
    public String buy(long userId , long productId){
        // 创建订单
        orderClient.create(userId , productId);
        // 扣减库存
        int a = 1/0;
        storageClient.changeStorage(userId , 1);
        return "ok";
    }

如此一来,就成功实现了分布式事务,其实我搭建过LCN,感觉LCN比Seata真是简单多少倍了



5. 聊聊原理

5.1 事务协调者-TC相关表

global_table:全局事务,每当有一个全局事务发起后,就会在该表中记录全局事务的ID
branch_table:分支事务,记录每一个分支事务的ID,分支事务操作的哪个数据库等信息
lock_table:全局锁

posted @ 2021-07-06 17:43   不将就鸭  阅读(756)  评论(0)    收藏  举报
刷新页面返回顶部
博客园  ©  2004-2025
浙公网安备 33010602011771号 浙ICP备2021040463号-3