学习地址:

源码分享:

分布式事务问题

分布式前

单机单库没这个问题

从1:1 -> 1:N -> N: N

分布式之后


单体应用被拆分成微服务应用,原来的三个模块被拆分成三个独立的应用,分别使用三个独立的数据源,业务操作需要调用三个服务来完成。此时每个服务内部的数据一致性由本地事务来保证, 但是全局的数据一致性问题没法保证。

用户购买:商品的业务逻辑。整个业务逻辑由3个微服务提供支持:
仓储服务:对给走的商品扣除仓储数量.
订单服务:根据采购需求创建订单。
帐户服务:从用户帐户中扣除余额.

一次业务操作需要跨多个数据源或需要跨多个系统进行远程调用,就会产生分布式事务问题

Seata

官网地址:http://seata.io/zh-cn/

Seata是一款开源的分布式事务解决方案,致力于在微服务架构下提供高性能和简单易用的分布式事务服务

分布式事务处理过程的一ID+三组件模型

Transaction ID XID --> 全局唯一的事务ID

3组件概念

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

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

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

处理过程

  1. TM 向 TC 申请开启一个全局事务,全局事务创建成功并生成一个全局唯一的 XID;
  2. XID 在微服务调用链路的上下文中传播;
  3. RM 向 TC 注册分支事务,将其纳入 XID 对应全局事务的管辖;
  4. TM 向 TC 发起针对 XID 的全局提交或回滚决议;
  5. TC 调度 XID 下管辖的全部分支事务完成提交或回滚请求。

使用

Spring 本地@Transactional

全局@GlobalTransactional

Seata原理

Simple Extensible Autonomous Transaction Architecture,简单可扩展自治事务框架

分布式事务的执行流程

  • TM开启分布式事务(TM向TC注册全局事务记录)

  • 换业务场景,编排数据库,服务等事务内资源(RM向TC汇报资源准备状态)

  • TM结束分布式事务,事务一阶段结束(TM通知TC提交/回滚分布式事务)

  • TC汇总事务信息,决定分布式事务是提交还是回滚

  • TC通知所有RM提交/回滚资源,事务二阶段结束。

AT模式如何做到对业务的无侵入


在一阶段, Seata会拦截“业务SQL"

解析SQL语义,找到"业务SQL"要更新的业务数据,在业务数据被更新前,将其保存成"before image"

执行"业务SQL"更新业务数据,在业务数据更新之后,

其保存成"after image” ,最后生成行锁。

以上操作全部在一个数据库事务内完成,这样保证了一阶段操作的原子性。


二阶段提交:

二阶段若是顺利提交的话,"业务SQL"在一阶段已经提交至数据库,所以Seata框架只需将一阶段的快照数据和行锁删掉,完成数据清理即可。


二阶段回滚:

二阶段如果是回滚的话,Seata就需要回滚一阶段已经执行的“业务SQL",还原业务数据。

回滚方式便是用"before image” 还原业务数据;但在还原前要首先要校验脏写,对比"数据库当前业务数据""和"after image" ,如果两份数据完全一致就说明没有脏写,可以还原业务数据,如果不一致就说明有脏写,出现脏写就需要转人工处理。


Seata-Server安装

下载地址:

演示版本:

将seata-server-.zip解压到指定目录

修改conf目录下的配置文件

备份、两个文件



driverClassName = ""
url = "jdbc:mysql://"
user = "root"
password = "8520"



创建数据库seata:https://github.com/seata/seata/blob/develop/script/server/db/mysql.sql

-- -------------------------------- The script used when storeMode is 'db' --------------------------------
-- the table to store GlobalSession data
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 = InnoDBDEFAULT 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 = InnoDBDEFAULT 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 = InnoDBDEFAULT CHARSET = utf8;

测试

先启动Nacos端口号8848

再启动seata-server

seata\bin -->


若启动报Data source rejected establishment of connection, message from server: "Too many connections"

找到mysql的安装目录,打开文件,找到max_connections配置,默认是100,设置成1000,重启mysql

一定要重新启动MYSQL才能生效

cmd下执行

net stop mysql
net start mysql

订单/库存/账户业务数据库准备

这里我们会创建三个服务,一个订单服务, 一个库存服务, 一个账户服务。

下订单-->扣库存-->减账户(余额)

当用户下单时,会在订单服务中创建一个订单,然后通过远程调用库存服务来扣减下单商品的库存,再通过远程调用账户服务来扣减用户账户里面的余额,最后在订单服务中修改订单状态为已完成。

该操作跨越二三个数据库,有两次远程调用,很明显会有分布式事务问题。

seata_order: 存储订单的数据库

seata_order库下建t_order表CREATE TABLE t_order(`id` BIGINT(11) NOT NULL AUTO_INCREMENT PRIMARY KEY,`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:已完结'
) ENGINE=INNODB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8;SELECT * FROM t_order;

seata_storage:存储库存的数据库

seata_storage库下建t_storage表CREATE TABLE t_storage(`id` BIGINT(11) NOT NULL AUTO_INCREMENT PRIMARY KEY,`product_id` BIGINT(11) DEFAULT NULL COMMENT '产品id',`total` INT(11) DEFAULT NULL COMMENT '总库存',`used` INT(11) DEFAULT NULL COMMENT '已用库存',`residue` INT(11) DEFAULT NULL COMMENT '剩余库存'
) ENGINE=INNODB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;INSERT INTO seata_storage.t_storage(`id`,`product_id`,`total`,`used`,`residue`)
VALUES('1','1','100','0','100');SELECT * FROM t_storage;

seata_account: 存储账户信息的数据库

seata_account库下建t_account表CREATE TABLE t_account(`id` BIGINT(11) NOT NULL AUTO_INCREMENT PRIMARY KEY COMMENT 'id',`user_id` BIGINT(11) DEFAULT NULL COMMENT '用户id',`total` DECIMAL(10,0) DEFAULT NULL COMMENT '总额度',`used` DECIMAL(10,0) DEFAULT NULL COMMENT '已用余额',`residue` DECIMAL(10,0) DEFAULT '0' COMMENT '剩余可用额度'
) ENGINE=INNODB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;INSERT INTO seata_account.t_account(`id`,`user_id`,`total`,`used`,`residue`) VALUES('1','1','1000','0','1000');SELECT * FROM t_account;

按照上述3库分别建对应的回滚日志表:https://github.com/seata/seata/blob/develop/script/client/at/db/mysql.sql

-- for AT mode you must to init this sql for you business database. the seata server not need it.
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 = InnoDBAUTO_INCREMENT = 1DEFAULT CHARSET = utf8 COMMENT ='AT transaction mode undo table';


订单/库存/账户业务微服务准备

seata-order-service2001

  1. 建module

  2. 写POM

seata版本对应

<?xml version="" encoding="UTF-8"?>
<project xmlns=""xmlns:xsi=""xsi:schemaLocation=" http://maven.apache.org/xsd/maven-4.0.0.xsd"><parent><artifactId>springcloud2020</artifactId><groupId>com.nuc.springcloud</groupId><version>-SNAPSHOT</version></parent><modelVersion>4.0.0</modelVersion><artifactId>seata-order-service2001</artifactId><dependencies><!--nacos--><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId></dependency><!--seata--><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-seata</artifactId><exclusions><exclusion><artifactId>seata-all</artifactId><groupId>io.seata</groupId></exclusion></exclusions></dependency><dependency><groupId>io.seata</groupId><artifactId>seata-all</artifactId><version></version></dependency><!--feign--><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-openfeign</artifactId></dependency><!--web-actuator--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-actuator</artifactId></dependency><!--mysql-druid--><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.15</version></dependency><dependency><groupId>com.alibaba</groupId><artifactId>druid-spring-boot-starter</artifactId><version>1.1.10</version></dependency><dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>2.0.0</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency></dependencies></project>
  1. 写YML

yml的tx-service-group: mobiw_tx_group与的对应

server:port: 2001spring:application:name: seata-order-servicecloud:alibaba:seata:#自定义事务组名称需要与seata-server中的对应tx-service-group: mobiw_tx_groupnacos:discovery:server-addr: localhost:8848datasource:driver-class-name: url: jdbc:mysql://localhost:3306/seata_order?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=GMT%2B8username: rootpassword: 8520feign:hystrix:enabled: falselogging:level:io:seata: infomybatis:mapperLocations: classpath:mapper/*.xml
  1. :https://github.com/seata/seata/blob/develop/script/client/conf/

yml的tx-service-group: mobiw_tx_group与的对应

transport {# tcp, unix-domain-sockettype = "TCP"#NIO, NATIVEserver = "NIO"#enable heartbeatheartbeat = true# the client batch send request enableenableClientBatchSendRequest = true#thread factory for nettythreadFactory {bossThreadPrefix = "NettyBoss"workerThreadPrefix = "NettyServerNIOWorker"serverExecutorThread-prefix = "NettyServerBizHandler"shareBossWorker = falseclientSelectorThreadPrefix = "NettyClientSelector"clientSelectorThreadSize = 1clientWorkerThreadPrefix = "NettyClientWorkerThread"# netty boss thread sizebossThreadSize = 1#auto default pin or 8workerThreadSize = "default"}shutdown {# when destroy server, wait secondswait = 3}serialization = "seata"compressor = "none"
}
service {#transaction service group mapping# mobiw_tx_group 与yml指定的一致= "default"#only support when registry.type=file, please don't set multiple addressesdefault.grouplist = ""#degrade, current not supportenableDegrade = false#disable seatadisableGlobalTransaction = false
}client {rm {asyncCommitBufferLimit = 10000lock {retryInterval = 10retryTimes = 30retryPolicyBranchRollbackOnConflict = true}reportRetryCount = 5tableMetaCheckEnable = falsereportSuccessEnable = falsesagaBranchRegisterEnable = false}tm {commitRetryCount = 5rollbackRetryCount = 5defaultGlobalTransactionTimeout = 60000degradeCheck = falsedegradeCheckPeriod = 2000degradeCheckAllowTimes = 10}undo {dataValidation = trueonlyCareUpdateColumns = truelogSerialization = "jackson"logTable = "undo_log"}log {exceptionRate = 100}
}
  1. :https://github.com/seata/seata/blob/develop/script/client/conf/

registry 使用的是 nacos , config 使用的是 file

registry {# file 、nacos 、eureka、redis、zk、consul、etcd3、sofa、customtype = "nacos"loadBalance = "RandomLoadBalance"loadBalanceVirtualNodes = 10nacos {application = "seata-server"serverAddr = ""group = "SEATA_GROUP"namespace = ""username = "nacos"password = "nacos"}eureka {serviceUrl = "http://localhost:8761/eureka"weight = "1"}redis {serverAddr = "localhost:6379"db = "0"password = ""timeout = "0"}zk {serverAddr = ""sessionTimeout = 6000connectTimeout = 2000username = ""password = ""}consul {serverAddr = ""}etcd3 {serverAddr = "http://localhost:2379"}sofa {serverAddr = ""region = "DEFAULT_ZONE"datacenter = "DefaultDataCenter"group = "SEATA_GROUP"addressWaitTime = "3000"}file {name = ""}custom {name = ""}
}config {# file、nacos 、apollo、zk、consul、etcd3、springCloudConfig、customtype = "file"nacos {serverAddr = ""namespace = ""group = "SEATA_GROUP"username = ""password = ""}consul {serverAddr = ""}apollo {appId = "seata-server"apolloMeta = ""namespace = "application"apolloAccesskeySecret = ""}zk {serverAddr = ""sessionTimeout = 6000connectTimeout = 2000username = ""password = ""}etcd3 {serverAddr = "http://localhost:2379"}file {name = ""}custom {name = ""}
}
  1. domain
//CommonResult
@Data
@AllArgsConstructor
@NoArgsConstructor
public class CommonResult<T> {private Integer code;private String message;private T data;public CommonResult(Integer code, String message) {this(code, message, null);}
}
//订单实体类
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Order {private Long id;private Long userId;private Long productId;private Integer count;private BigDecimal money;private Integer status; //订单状态:0:创建中;1:已完结
}
  1. Dao接口及实现
@Mapper
public interface OrderDao {//1 新建订单void create(Order order);//2 修改订单状态,从0到1void update(@Param("userId") Long userId, @Param("status")Integer status);
}
<?xml version="" encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""">
<mapper namespace=""><resultMap type=""><id column="id" property="id" jdbcType="BIGINT"/><result column="user_id" property="userId" jdbcType="BIGINT"/><result column="product_id" property="productId" jdbcType="BIGINT"/><result column="count" property="count" jdbcType="INTEGER"/><result column="money" property="money" jdbcType="DECIMAL"/><result column="status" property="status" jdbcType="INTEGER"/></resultMap><insert>insert into t_order (id, user_id, product_id, count, money, status)values (null, #{userId}, #{productId}, #{count}, #{money}, 0);</insert><update>update t_orderset status = 1where user_id = #{userId}and status = #{status};</update>
</mapper>
  1. Service接口及实现
//订单
public interface OrderService {void create(Order order);
}
@Service
@Slf4j
public class OrderServiceImpl implements OrderService {@Resourceprivate OrderDao orderDao;@Resourceprivate StorageService storageService;@Resourceprivate AccountService accountService;/*** 创建订单->调用库存服务扣减库存->调用账户服务扣减账户余额->修改订单状态* @param order*/@Overridepublic void create(Order order) {//1. 新建订单("----->开始新建订单");(order);//2. 扣减库存("----->订单微服务开始调用库存,做扣减");((), ());("----->订单微服务开始调用库存,做扣减end");//3. 扣减账户("----->订单微服务开始调用账户,做扣减");((),());("----->订单微服务开始调用账户,做扣减end");//4. 修改订单的状态 从0到1   1代表完成("----->修改订单状态开始");((),0);("----->修改订单状态完成");("----->下订单结束了,O(∩_∩)O哈哈~");}
}
//使用feign调用AccountService
@FeignClient(value = "seata-account-service")
public interface AccountService {@PostMapping(value = "/account/decrease")CommonResult decrease(@RequestParam("userId") Long userId, @RequestParam("money") BigDecimal money);}
//使用feign调用StorageService
@FeignClient(value = "seata-storage-service")
public interface StorageService {@PostMapping(value = "/storage/decrease")CommonResult decrease(@RequestParam("productId") Long productId, @RequestParam("count") Integer count);
}
  1. Controller
@RestController
public class OrderController {@Resourceprivate OrderService orderService;@GetMapping("/order/create")public CommonResult create(Order order) {(order);return new CommonResult(200, "订单创建成功");}
}
  1. Config配置
@Configuration
@MapperScan({""})
public class MyBatisConfig {}
@Configuration
public class DataSourceProxyConfig {@Value("${}")private String mapperLocations;@Bean@ConfigurationProperties(prefix = "")public DataSource druidDataSource() {return new DruidDataSource();}@Beanpublic DataSourceProxy dataSourceProxy(DataSource dataSource) {return new DataSourceProxy(dataSource);}@Beanpublic SqlSessionFactory sqlSessionFactoryBean(DataSourceProxy dataSourceProxy) throws Exception {SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();(dataSourceProxy);(new PathMatchingResourcePatternResolver().getResources(mapperLocations));(new SpringManagedTransactionFactory());return sqlSessionFactoryBean.getObject();}}
  1. 主启动
@EnableDiscoveryClient
@EnableFeignClients
@SpringBootApplication(exclude = )//取消数据源自动创建的配置
public class SeataOrderMainApp2001 {public static void main(String[] args) {(SeataOrderMainApp2001.class, args);}
}

seata-storage-service2002

  1. 建module

  2. 写POM

与2001一致

  1. 写YML

与2001相似,修改端口2002、修改数据库seata_storage

与2001一致

与2001一致

  1. domain

CommonResult与2001一致

@Data
public class Storage {private Long id;// 产品idprivate Long productId;//总库存private Integer total;//已用库存private Integer used;//剩余库存private Integer residue;
}
  1. Dao接口及实现
@Mapper
public interface StorageDao {void decrease(@Param("productId") Long productId, @Param("count") Integer count);
}
<?xml version="" encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""">
<mapper namespace=".StorageDao"><resultMap type=""><id column="id" property="id" jdbcType="BIGINT"/><result column="product_id" property="productId" jdbcType="BIGINT"/><result column="total" property="total" jdbcType="INTEGER"/><result column="used" property="used" jdbcType="INTEGER"/><result column="residue" property="residue" jdbcType="INTEGER"/></resultMap><update>update t_storageset used    = used + #{count},residue = residue - #{count}where product_id = #{productId};</update>
</mapper>
  1. Service接口及实现
public interface StorageService {// 扣减库存void decrease(Long productId, Integer count);
}
@Service
public class StorageServiceImpl implements StorageService {private static final Logger LOGGER = ();@Resourceprivate StorageDao storageDao;// 扣减库存@Overridepublic void decrease(Long productId, Integer count) {("------->storage-service中扣减库存开始");(productId,count);("------->storage-service中扣减库存结束");}
}
  1. Controller
@RestController
public class StorageController {@Autowiredprivate StorageService storageService;//扣减库存@RequestMapping("/storage/decrease")public CommonResult decrease(Long productId, Integer count) {(productId, count);return new CommonResult(200, "扣减库存成功!");}
}
  1. Config配置

与2001一致

  1. 主启动
@SpringBootApplication(exclude = )
@EnableDiscoveryClient
@EnableFeignClients
public class SeataStorageMainApp2002 {public static void main(String[] args) {(SeataStorageMainApp2002.class, args);}
}

seata-account-service2003

  1. 建module

  2. 写POM

与2001一致

  1. 写YML

与2001相似,修改端口2003、修改数据库seata_account

与2001一致

与2001一致

  1. domain

CommonResult与2001一致

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Account {private Long id;/*** 用户id*/private Long userId;/*** 总额度*/private BigDecimal total;/*** 已用额度*/private BigDecimal used;/*** 剩余额度*/private BigDecimal residue;
}
  1. Dao接口及实现
@Mapper
public interface AccountDao {/*** 扣减账户余额*/void decrease(@Param("userId") Long userId, @Param("money") BigDecimal money);
}
<?xml version="" encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""">
<mapper namespace=".AccountDao"><resultMap type=""><id column="id" property="id" jdbcType="BIGINT"/><result column="user_id" property="userId" jdbcType="BIGINT"/><result column="total" property="total" jdbcType="DECIMAL"/><result column="used" property="used" jdbcType="DECIMAL"/><result column="residue" property="residue" jdbcType="DECIMAL"/></resultMap><update>UPDATE t_accountSET residue = residue - #{money},used    = used + #{money}WHERE user_id = #{userId};</update></mapper>
  1. Service接口及实现
public interface AccountService {/*** 扣减账户余额*/void decrease(@RequestParam("userId") Long userId, @RequestParam("money") BigDecimal money);
}
@Service
public class AccountServiceImpl implements AccountService {private static final Logger LOGGER = ();@ResourceAccountDao accountDao;/*** 扣减账户余额*/@Overridepublic void decrease(Long userId, BigDecimal money) {("------->account-service中扣减账户余额开始");(userId, money);("------->account-service中扣减账户余额结束");}
}
  1. Controller
@RestController
public class AccountController {@ResourceAccountService accountService;/*** 扣减账户余额*/@RequestMapping("/account/decrease")public CommonResult decrease(@RequestParam("userId") Long userId, @RequestParam("money") BigDecimal money) {(userId, money);return new CommonResult(200, "扣减账户余额成功!");}
}
  1. Config配置

与2001一致

  1. 主启动
@SpringBootApplication(exclude = )
@EnableDiscoveryClient
@EnableFeignClients
public class SeataAccountMainApp2003 {public static void main(String[] args) {(SeataAccountMainApp2003.class, args);}
}

测试

数据库初始状态


下单

http://localhost:2001/order/create?userId=1&productId=1&count=10&money=100


查看数据库


超时异常,没加@GlobalTransactional

修改2003

@Service
public class AccountServiceImpl implements AccountService {private static final Logger LOGGER = ();@ResourceAccountDao accountDao;/*** 扣减账户余额*/@Overridepublic void decrease(Long userId, BigDecimal money) {("------->account-service中扣减账户余额开始");try {(20);} catch (InterruptedException e) {();}(userId, money);("------->account-service中扣减账户余额结束");}
}

重启2003再次测试



超时异常,添加@GlobalTransactional

修改2001 OrderServiceImpl

public class OrderServiceImpl implements OrderService {@Override@GlobalTransactional(name = "mobiw-create-order",rollbackFor = )public void create(Order order) {...}
}

重启2001,再次测试

没有写入数据库

宝塔mysql 分布式_Seata处理分布式事务 - 魔笔钨丝浣相关推荐

  1. gateway sentinel 熔断 不起作用_Sentinel实现熔断与限流 - 魔笔钨丝浣

    学习地址:https://www.bilibili.com/video/BV18E411x7eT?p=111 Sentinel 官网地址: 中文文档:/wiki/介绍 随着微服务的流行,服务和服务之间 ...

  2. ecs 对比 本地服务器_ECS训练营Day03_SLB负载均衡实践 - 魔笔钨丝浣

    创建资源 云服务器状态确认 分别访问实验提供的两台云服务器ECS,观察有什么异同点. 打开浏览器,在输入框中输入 云产品资源 提供的 云服务器ECS-1 的 弹性IP 说明:正常情况下不会显示 后端服 ...

  3. MySQL高级特性之分布式(XA)事务的介绍

    文章目录 分布式(XA) 事务 1.内部XA事务 2.外部XA事务 这一部分是 高性能MySQL(第3版)中第七章第11节内容:关于MySQL高级特性之分布式(XA)事务的介绍,(不感兴趣的可以忽略) ...

  4. 一文搞懂MySQL XA如何实现分布式事务

    一文搞懂MySQL XA如何实现分布式事务 前言 XA 协议 如何通过MySQL XA实现分布式事务 前言 MySQL支持单机事务的良好表现毋庸置疑,那么在分布式系统中,涉及多个节点,MySQL又是如 ...

  5. MySQL分布式(XA)事务

    本文内容来自<高性能MySQL>,自己整理了一下. 存储引擎的事务特性能够保证在存储引擎级别实现ACID.而分布式事务则让存储引擎级别的ACID可以扩展到数据库层面,甚至可以扩展到多个数据 ...

  6. 分布式mysql保持数据一致性_一种分布式跨数据库保持事务一致性的方法及系统与流程...

    本发明涉及计算机网络技术领域,尤其涉及一种分布式跨数据库保持事务一致性的方法及系统. 背景技术: 目前,分布式服务架构十分的流行,特别是对于电商领域来说.他的优点就不多说了,但是随着集群机器的增加,集 ...

  7. 基于mysql和php的分布式事务处理1,基于MySQL和PHP的分布式事务处理

    PHP(超级文本预处理语言)是一种基于服务器端.执行效率高且易于开发的HTML内嵌式语言,是用户生成动态网页的工具之一[1].MySQL是全球最受欢迎的小型开放源码的关系型数据库管理系统,其体积小.速 ...

  8. RadonDB - 基于MySQL的云原始分布式数据库,用于构建全局的,可扩展的云服务

    RadonDB是开源的云原生MySQL数据库,用于构建全局的,可扩展的云服务 概观 RadonDB是一个开源的,云原生的MySQL数据库,可实现无限的可扩展性和性能. 什么是RadonDB? Rado ...

  9. 分布式锁和分布式事务和分布式Session

    分布式锁和分布式事务 分布式锁 基于MySQL数据库 这种方式一般采用数据库乐观锁,不推荐使用 基于redis 加锁:setnx命令,key是锁的唯一标志,可以为想要加锁的资源设置对应的key,val ...

最新文章

  1. Python 频繁读取Mysql相关问题
  2. c语言静态存储和动态存储,C语言中的静态内存与动态内存
  3. 如何降低遮挡对人脸识别的影响
  4. mysql学习整理(一)
  5. ubuntu7.10安装到3D开启
  6. java arraylist 合并_在Java中将两个arrayList合并到一个新的arrayList中,没有重复且没有顺序...
  7. Composer 安装时要求输入授权用户名密码
  8. 生猪价格matlab,猪肉价格的数学模型.doc
  9. canvas_requestAnimationFrame帧率显示fps
  10. expdp/impdp 使用总结
  11. WPS JS宏表格定位实例
  12. 微程序控制器的基本原理
  13. 黑苹果记(ThinkPad X200)
  14. 画一个客户画像系统架构图
  15. 解压特定zip压缩文件中特定文件,Java
  16. LeetCode | 218. 天际线问题
  17. 网络安全框架:OWASP概述、优势、实践和局限性分析
  18. 微信小程序van-tabs动态添加标签
  19. HTC G13电池怎么鉴别真伪
  20. (附源码)计算机毕业设计ssm大学生兼职平台

热门文章

  1. VSCode智能补全代码片段技巧
  2. 论文矢量图导出踩坑记 (含Word/latex + Visio, Adobe Acrobat Pro, Matlab等)
  3. JVM 垃圾收集器 G1 详解
  4. [Golang]力扣Leetcode - 852. 山脉数组的峰顶索引(二分查找)
  5. GitHub加速插件
  6. 王玫泰山医学院计算机,计算机专业PBL对提高团队协作能力的研究.pdf
  7. python爬虫网页表格_python网页表格
  8. 颜色以红色、品红、蓝紫色、绿/黄、蓝、桔红和青色排序
  9. mysql 哨兵模式_redis配置主从+sentine哨兵模式
  10. YCM(YouCompleteMe)资源下载,包含了全部的插件及依赖子模块