大白话分布式事务-SEATA事务四种模式(内含demo)

因为这里主要是讲解分布式事务,关于什么是事务,以及事务的特性,单个事务的使用方式,以及在Spring框架下,事务的传播方式,这里就不再赘述了。但是我这里要补充一点就是,一提到事务大家脑子里第一闪念就是传统意义上的事务,就是指数据库上的事务,实则在我们正常的开发中,更多的关注的是数据的一致性。

但是又因为我们的数据不仅仅是存储在数据库上,也可能是别的组件上,或者是别的不支持事务的数据库上。时至今日,我们的“事务”的概念已经逐渐泛化掉了,在很多场景下,如果只是讲我们的数据库的事务显得过于狭隘,终极目的就是为了保持数据的一致性。因为数据的事务比较有代表性,而且每一位开发的同学都是从这里接触的事务,所以我下面也主要以数据库来讲,但不仅仅是指数据库的事务。

数据库的事务的生效范围是数据库连接级别的。我们先说一下这里的连接是什么意思,无论是我们通过navicat或者别的方式,比如JDBC操作数据库,其实都是基于连接。我想大家一定要理解“事务基于连接而不是基于数据库”这句话的意思,就是说即便我操作的是同一个数据库,不同连接上的事务仍然是两个事务,而不是说我只有都对两个不同的数据库的操作才是两个事务。

  • 当我们使用navicat的时候,我们连上数据库开的窗口那就是一个数据库连接

navicat数据库连接

  • JDBC中先获取数据库连接才能开启并操作事务
try {
   
   
    Connection connection = DriverManager.getConnection(URL, USER, PASSWORD);

    // 禁用自动提交,开始手动管理事务
    connection.setAutoCommit(false);
    
    // 执行DML
    String deductSql = "UPDATE account SET balance = balance - ? WHERE account_id = ?";
    deductStmt = connection.prepareStatement(deductSql);
    deductStmt.setDouble(1, 100); // 扣减金额
    deductStmt.setInt(2, 1); // 账户A的ID
    deductStmt.executeUpdate();
    
    // 提交事务 
    connection.commit();
} catch (Exception e) {
   
   
    
    // 出现异常回滚
    connection.rollback();
}

从上面大家已经知道事务是基于连接级别的。也就是说在一个连接上某一瞬间同时只能有一个事务存在。当你在两个不同的连接上同时操作两个事务,两个事务是相互独立的,但是效果都会体现在数据库上。如果是两个事务同时开启并操作相同的数据,这种冲突当前的数据库会根据设置的隔离级别来进行处理,由数据库本身保证。

上面已经说了我们的事务都是基于连接的。如果在一次请求进来,在处理这一个请求期间,我们多次执行数据库操作,如果每次操作数据库的时候每次都会重新获取新的连接(现在大家都会配置数据库连接池,所以存在多次获取同一个连接的可能性),无论获取的是不是同一个数据库连接,只要是设置的是自动提交,数据库连接默认都是自动提交,就没办法保证整个请求生命周期中的数据符合事务一致性。

想必大家已经明白事务是基于连接的意义了,而连接又是面向数据库的。如果我们在一次请求中操作了同一个数据库,其中的事务我们叫做集中式事务,这个是比较好保证的,只需要我们在整个请求过程中保持使用的是同一个数据库连接,如果全部执行成功就commit,所有的数据库操作全部生效。如果失败就rollback,所有的数据库操作全部失效。本质可以参考上面的jdbc demo,其实Spring框架的事务就是基于这个原理做的。详情可以看大白话讲解Spring对数据源和事务管理以及多数据源配置

众所周知(你真知道吗?评论区告诉我),Spring的事务是基于事务管理器 @Transactional(transactionManager = "primaryTransactionManager"),事务管理器是基于数据源。

// 配置主数据源的事务管理器
@Primary
@Bean(name = "primaryTransactionManager")
public DataSourceTransactionManager primaryTransactionManager(
        @Qualifier("primaryDataSource") DataSource dataSource) {
   
   
    return new DataSourceTransactionManager(dataSource);
}

Spring支持多个数据源的配置,也就支持多个事务管理器,也就支持多个不同数据源的事务,但是没办法保证不同数据源的事务保持一致,不同的数据源是在不同的事务上下文处理的,这种情况它们已经是分布式事务了。

如果是操作了不同的数据库,要么对所有数据库的操作全部成功,要么对所有数据库的操作全部回滚,这种场景的事务,我们叫做分布式事务。解释下,分布式事务是我们需要保证全局的一致性,无论是不是数据库,有可能我们把数据更新到Redis、Mongo或者别的保存数据状态的组件上。所以分布式事务只是一种对数据状态全局一致性的保证。

分布式事务,目前市场上比较成熟的阿里的开源的 SEATA分布式事务框架

分布式事务的四种模式:

四种模式对比

模式 一致性 适用场景 性能 开发复杂度 数据库支持
AT 最终一致性 数据库为主的简单场景 关系型数据库
TCC 强一致性 复杂业务逻辑 较高 多种存储类型
SAGA 最终一致性 长事务、异步场景 较高 较高 不限
XA 强一致性 金融类强一致性需求 较低 支持 XA 的数据库

AT模式

AT 模式是 Seata 中的核心模式,适用于关系型数据库的分布式事务处理。以下是一个使用 AT 模式的简单示例,演示如何在 Spring Boot 中集成 Seata 来处理分布式事务。假设有两个微服务 order-serviceaccount-service,它们分别负责订单处理和账户扣款。

1. 环境准备

  • Spring Boot
  • MySQL
  • Seata Server
  • Nacos(用于服务发现和配置管理)

2. 项目结构

假设我们有两个模块:

  • order-service:负责生成订单
  • account-service:负责扣减账户余额

每个服务都使用 Spring Boot 和 MySQL,并且通过 Seata 来保证分布式事务的一致性。

seata-demo/
├── account-service
├── order-service
└── seata-server (单独运行的 Seata Server)

3. 依赖引入

每个微服务都需要引入以下依赖:

<dependency>
    <groupId>io.seata</groupId>
    <artifactId>seata-spring-boot-starter</artifactId>
    <version>1.6.1</version>
</dependency>

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>

4. 配置 Seata

order-serviceaccount-serviceapplication.yml 中添加 Seata 和 Nacos 的配置。
spring:
  application:
    name: order-service
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848
  datasource:
    url: jdbc:mysql://localhost:3306/order_db
    username: root
    password: root
    driver-class-name: com.mysql.cj.jdbc.Driver
    type: com.zaxxer.hikari.HikariDataSource
  seata:
    tx-service-group: my_test_tx_group

feign:
  discovery:
    enabled: true

server:
  port: 8081

seata:
  enabled: true
  service:
    vgroup-mapping:
      my_test_tx_group: "default"
    disable-global-transaction: false

5. 数据库表结构

order 表(订单服务中的订单表)
CREATE TABLE `order_tbl` (
  `id` BIGINT(20) NOT NULL AUTO_INCREMENT,
  `user_id` VARCHAR(255) DEFAULT NULL,
  `commodity_code` VARCHAR(255) DEFAULT NULL,
  `count` INT(11) DEFAULT NULL,
  `money` DECIMAL(11, 0) DEFAULT NULL,
  PRIMARY KEY (`id`)
);
account 表(账户服务中的账户表)
CREATE TABLE `account_tbl` (
  `id` BIGINT(20) NOT NULL AUTO_INCREMENT,
  `user_id` VARCHAR(255) DEFAULT NULL,
  `money` DECIMAL(11, 0) DEFAULT NULL,
  PRIMARY KEY (`id`)
);

6. Order Service 实现

order-service 中实现订单创建逻辑,并使用 Seata 的全局事务。
@RestController
@RequestMapping("/order")
public class OrderController {
   
   

    @Autowired
    private OrderService orderService;

    @PostMapping("/create")
    public String create(@RequestParam("userId") String userId, 
                         @RequestParam("commodityCode") String commodityCode, 
                         @RequestParam("count") Integer count, 
                         @RequestParam("money") BigDecimal money) {
   
   
        orderService.createOrder(userId, commodityCode, count, money);
        return "Order created successfully!";
    }
}

@Service
public class OrderService {
   
   

    @Autowired
    private OrderRepository orderRepository;

    @Autowired
    private AccountFeignClient accountFeignClient;

    @GlobalTransactional(name = "create-order", rollbackFor = Exception.class)
    public void createOrder(String userId, String commodityCode, Integer count, BigDecimal money
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值