为了账号安全,请及时绑定邮箱和手机立即绑定

Seata和Mysql存储演示项目实战教程

概述

本文详细介绍了Seata与MySQL存储的集成,包括Seata的基本概念、服务端部署、连接配置以及与Spring Boot项目的整合。通过具体示例演示了如何使用Seata管理分布式事务,确保数据的一致性和可靠性。文章还提供了手动编排事务流程和进行事务回滚与补偿操作的实战代码示例。本文旨在帮助读者掌握Seata和MySQL存储演示项目实战。

Seata简介与基本概念
什么是Seata

Seata(Software Transaction Access Layer)是一个开源的分布式事务解决方案,旨在帮助用户在微服务架构中实现高性能的分布式事务管理。Seata支持XA、TCC、SAGA、可靠消息等分布式事务模式,但最常用的是TCC模式。通过Seata,开发人员可以在微服务架构中轻松地实现分布式事务的管理和控制,确保数据的一致性和可靠性。

Seata的核心组件介绍

Seata的核心组件包括以下几个部分:

  1. Transaction Service:负责分布式事务的全局管理和协调。它管理来自不同应用的服务注册和事务的提交或回滚请求。

  2. Transaction Log:存储全局事务状态的数据库,用于持久化存储全局事务的执行状态。Seata默认使用MySQL、Oracle、TiDB等作为存储,也可使用文件或Redis等其他存储方式。

  3. Transaction Manager:提供分布式事务的全局协调服务,包括事务的注册、提交、回滚等操作。

  4. Resource Service:与应用中的数据源绑定,执行本地事务提交或回滚。Seata通过代理类和切面增强技术,在本地数据库操作中插入事务控制逻辑,从而实现对分布式事务的管理。

  5. Transaction Coordinator:协调全局事务的执行,根据全局事务的执行情况,决定事务的最终状态并通知各个局部事务执行相应操作。
Seata的工作原理概述

Seata的工作流程如下:

  1. 全局事务开启:服务端接收到客户端的全局事务开启请求后,将全局事务注册到事务管理器中,并返回一个全局事务标识(XID)给客户端。

  2. 本地操作:客户端发起数据库操作,业务逻辑中调用Seata提供的代理类或切面增强方法,执行本地事务操作。

  3. 本地事务提交:当本地事务执行完成后,Seata会封装一个本地事务提交请求,发送给事务管理器。

  4. 提交/回滚决定:事务管理器根据各个局部事务的状态,决定全局事务的最终状态(提交或回滚),并将决定发送给各个局部事务服务。

  5. 局部事务执行:各局部事务服务根据事务管理器的决定执行提交或回滚操作。

  6. 事务状态持久化:事务状态信息会被持久化到Seata的存储模块中,以便后续的事务状态查询和回查操作。
Mysql数据库基础
Mysql数据库安装与配置

MySQL是一个开源的数据库管理系统,广泛应用于各种规模的应用程序中。以下是MySQL的安装与配置步骤:

安装MySQL

根据操作系统不同,安装MySQL的方法也不同。这里以Ubuntu为例,介绍如何安装MySQL:

sudo apt update
sudo apt install mysql-server

安装完成后,MySQL服务器会自动启动。

配置MySQL

配置MySQL需要编辑配置文件my.cnf,该文件通常位于/etc/mysql/my.cnf。以下是一些常见的配置项:

[mysqld]
bind-address = 0.0.0.0
max_connections = 200
  • bind-address:设置MySQL监听的IP地址,0.0.0.0表示监听所有网卡。
  • max_connections:设置MySQL的最大连接数。

配置完成后,重启MySQL服务:

sudo systemctl restart mysql

初始化MySQL

MySQL安装完成后,需要初始化数据库并设置root用户密码:

sudo mysql_secure_installation

运行上述命令后,会提示设置root用户的密码,并进行一些安全设置。

Mysql基本操作与SQL语句

MySQL支持多种基本的操作,包括创建数据库、创建表、插入数据、查询数据、更新数据和删除数据等。以下是具体的SQL语句示例:

创建数据库

CREATE DATABASE mydb;

创建表

CREATE TABLE users (
    id INT AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(50),
    email VARCHAR(100)
);

插入数据

INSERT INTO users (name, email) VALUES ('张三', 'zhangsan@example.com');
INSERT INTO users (name, email) VALUES ('李四', 'lisi@example.com');

查询数据

SELECT * FROM users;
SELECT name, email FROM users WHERE id = 1;

更新数据

UPDATE users SET email = 'zhangsan_new@example.com' WHERE id = 1;

删除数据

DELETE FROM users WHERE id = 1;

这些基础操作可以满足大部分日常数据库操作需求。

Seata与Mysql环境搭建
Seata服务端部署

Seata服务端需要部署在服务器上,以提供分布式事务管理服务。以下是部署步骤:

下载Seata

首先,从Seata官方网站下载最新版本的Seata服务端包。

wget https://github.com/seata/seata/releases/download/1.6.1/seata-server-1.6.1.tar.gz
tar -xvf seata-server-1.6.1.tar.gz
cd seata-server-1.6.1

配置Seata服务端

Seata服务端的配置文件位于conf/registry.confconf/seata.conf。在registry.conf中配置注册中心:

registry {
  # file  registry
  file {
    # 当注册中心为file时,需要配置store, path和registry的store目录为同一个即可
    store {
      # type
      dir = "registry"
    }
  }
}

seata.conf中配置事务管理服务端:

service {
  vgroup_mapping.default_group = "default"
  # service
  registry = "file"
  application_id = "seata_example"
  tx_service_group = "default_group"
  ...
}

启动Seata服务端

执行以下命令启动Seata服务端:

sh ./bin/seata-server.sh -p 8091 -m db

参数说明:

  • -p 8091:设置Seata服务端监听的端口。
  • -m db:指定配置模式为db模式(使用数据库存储事务信息)。
配置Seata连接Mysql

Seata使用MySQL作为事务日志的存储,需要配置Seata连接MySQL数据库。

创建MySQL存储表

创建MySQL数据库并初始化Seata需要的存储表:

CREATE DATABASE seata;
USE seata;

CREATE TABLE `branch_table` (
  `id` VARCHAR(128) NOT NULL,
  `xid` VARCHAR(128) NOT NULL,
  `transaction_id` BIGINT(20) DEFAULT NULL,
  `resource_group_id` VARCHAR(32) DEFAULT NULL,
  `branch_id` BIGINT(20) NOT NULL,
  `branch_transaction_status` VARCHAR(8) DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `uk_txid` (`xid`,`branch_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

CREATE TABLE `global_table` (
  `xid` VARCHAR(128) NOT NULL,
  `transaction_id` BIGINT(20) DEFAULT NULL,
  `status` INT(11) DEFAULT NULL,
  `application_id` VARCHAR(32) DEFAULT NULL,
  `transaction_service_group` VARCHAR(32) DEFAULT NULL,
  `transaction_name` VARCHAR(32) DEFAULT NULL,
  `timeout` INT(11) DEFAULT NULL,
  `business_key` VARCHAR(256) DEFAULT NULL,
  `gmt_create` DATETIME DEFAULT NULL,
  `gmt_modified` DATETIME DEFAULT NULL,
  `retry_count` INT(11) DEFAULT 0,
  `status_desc` VARCHAR(8) DEFAULT NULL,
  PRIMARY KEY (`xid`),
  KEY `idx_status_gmt_modified` (`status`,`gmt_modified`),
  KEY `idx_gmt_create` (`gmt_create`),
  KEY `idx_transaction_id` (`transaction_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

CREATE TABLE `lock_table` (
  `row_key` VARCHAR(128) NOT NULL,
  `xid` VARCHAR(128) DEFAULT NULL,
  `lock_key` VARCHAR(128) DEFAULT NULL,
  `branch_id` INT(11) NOT NULL,
  `lock_status` INT(11) DEFAULT NULL,
  `lock_record_id` BLOB,
  `lock_record_key` VARCHAR(32) DEFAULT NULL,
  `tenant_id` VARCHAR(128) DEFAULT '',
  PRIMARY KEY (`row_key`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

配置Seata连接MySQL

conf/seata.conf文件中配置MySQL连接信息:

store {
  mode = "db"
  # 数据源,此处使用MySQL
  db {
    datasource = "druid"
    driverClassName = "com.mysql.jdbc.Driver"
    url = "jdbc:mysql://localhost:3306/seata?characterEncoding=utf8"
    username = "seata"
    password = "123456"
  }
}

测试配置

确保Seata服务端连接到MySQL数据库成功。可以通过Seata服务端的控制台进行查看事务日志,或者通过MySQL客户端查询seata数据库中的表来验证。

Seata和Mysql集成示例
创建分布式事务示例项目

创建Spring Boot项目

使用Spring Initializr创建一个Spring Boot项目,并引入Seata相关依赖:

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

配置Seata

在项目的application.yml文件中配置Seata:

seata:
  transaction:
  group: default_group
  enabled: true
  application-id: seata_example
  server:
    enable: true
    port: 8091
    mode: db
    store:
      db:
        datasource:
          driverClassName: com.mysql.jdbc.Driver
          url: jdbc:mysql://localhost:3306/seata?characterEncoding=utf8
          username: seata
          password: 123456

创建业务服务类

创建一个服务类,模拟业务操作:

@Service
public class OrderService {

  @Autowired
  private OrderMapper orderMapper;

  @GlobalTransactional(name = "order_create", transactionTryTimeout = 30)
  public void createOrder(Order order) {
    orderMapper.insert(order);
  }
}

创建数据访问层

创建数据访问层,用于与数据库交互:

@Repository
public interface OrderMapper extends JpaRepository<Order, Long> {
  // 自定义SQL语句
  @Query("SELECT o FROM Order o WHERE o.id = :id")
  Optional<Order> findById(@Param("id") Long id);
}

创建实体类

定义一个实体类Order,表示订单:

@Entity
public class Order {

  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  private Long id;

  private String orderId;

  private String userId;

  private String productCode;

  private BigDecimal amount;

  // Getter and Setter
}

测试分布式事务

创建一个控制器测试分布式事务功能:

@RestController
public class OrderController {

  @Autowired
  private OrderService orderService;

  @PostMapping("/createOrder")
  public String createOrder(@RequestBody Order order) {
    orderService.createOrder(order);
    return "Order created successfully";
  }
}

启动项目并访问createOrder接口,验证Seata是否正确管理了分布式事务。

配置Seata的事务管理

配置全局事务

在服务类中使用@GlobalTransactional注解标记需要分布式事务支持的方法:

@Service
public class OrderService {

  @GlobalTransactional(name = "order_create", transactionTryTimeout = 30)
  public void createOrder(Order order) {
    orderMapper.insert(order);
  }
}

配置业务超时时间

@GlobalTransactional注解中可以配置事务尝试超时时间,防止事务长时间等待:

@Service
public class OrderService {

  @GlobalTransactional(name = "order_create", transactionTryTimeout = 30)
  public void createOrder(Order order) {
    orderMapper.insert(order);
  }
}

配置事务隔离级别

可以通过配置文件设置全局事务的隔离级别:

seata:
  transaction:
    isolation-level: SERIALIZABLE
使用Seata管理Mysql中的事务

配置数据源代理

在Spring Boot的配置文件中配置Seata的数据源代理:

seata:
  transaction:
    enabled: true
    application-id: seata_example
    transaction-service-group: default_group
    service:
      # transaction retry times
      retryTimes: 2
    datasource:
      # MySQL
      driverClassName: com.mysql.jdbc.Driver
      url: jdbc:mysql://localhost:3306/mydb?characterEncoding=utf8
      username: root
      password: root

使用Seata事务注解

在业务服务类中使用Seata的事务注解:

@Service
public class OrderService {

  @Autowired
  private OrderMapper orderMapper;

  @GlobalTransactional(name = "order_create", transactionTryTimeout = 30)
  public void createOrder(Order order) {
    try {
      // 模拟业务操作
      orderMapper.insert(order);
      // 模拟异常
      int i = 1 / 0;
    } catch (Exception e) {
      // 处理异常
      throw new RuntimeException("Order creation failed", e);
    }
  }
}

通过这种方式,可以确保业务操作在分布式环境下的一致性和可靠性。

Seata与Mysql存储案例实践
分布式事务场景模拟

为了更好地理解Seata在实际项目中的应用,我们来模拟一个订单系统中的分布式事务场景。

场景描述

在一个典型的电商系统中,订单和库存管理通常分布在不同的服务中,需要保证订单创建和库存扣减操作的一致性。具体场景如下:

  1. 订单创建:当用户下单时,需要创建一个订单记录。
  2. 库存扣减:创建订单后,需要扣减对应的库存数量。

项目结构

项目结构如下:

seata-order-example
|-- src/main/java/com/example/
|   |-- OrderService.java
|   |-- StockService.java
|-- src/main/resources/
|   |-- application.yml

代码实现

OrderService.java

@Service
public class OrderService {

  @Autowired
  private OrderMapper orderMapper;

  @Autowired
  private StockService stockService;

  @GlobalTransactional(name = "order_create", transactionTryTimeout = 60)
  public void createOrder(Order order, String productId, int quantity) {
    // 创建订单
    orderMapper.insert(order);

    // 扣减库存
    stockService.decreaseStock(productId, quantity);
  }
}

StockService.java

@Service
public class StockService {

  @Autowired
  private StockMapper stockMapper;

  @Transactional
  public void decreaseStock(String productId, int quantity) {
    // 扣减库存
    stockMapper.decreaseStock(productId, quantity);
  }
}

OrderMapper.java

@Repository
public interface OrderMapper extends JpaRepository<Order, Long> {
  @Query("SELECT o FROM Order o WHERE o.id = :id")
  Optional<Order> findById(@Param("id") Long id);
}

StockMapper.java

@Repository
public interface StockMapper {

  @Query("UPDATE Stock SET quantity = quantity - :quantity WHERE productId = :productId AND quantity > :quantity")
  int decreaseStock(@Param("productId") String productId, @Param("quantity") int quantity);
}

Order.java

@Entity
public class Order {

  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  private Long id;

  private String orderId;

  private String userId;

  private String productCode;

  private BigDecimal amount;

  // Getter and Setter
}

Stock.java

@Entity
public class Stock {

  @Id
  private String productId;

  private int quantity;

  // Getter and Setter
}

测试场景

创建一个控制器接口来测试订单创建和库存扣减的操作:

@RestController
public class OrderController {

  @Autowired
  private OrderService orderService;

  @PostMapping("/createOrder")
  public String createOrder(@RequestBody Order order, @RequestParam String productId, @RequestParam int quantity) {
    orderService.createOrder(order, productId, quantity);
    return "Order created successfully";
  }
}

通过访问createOrder接口,可以测试订单创建和库存扣减操作是否满足分布式事务的约束。

手动编排事务流程

除了使用Seata提供的注解方式来管理事务,还可以手动编排事务流程。

手动开启事务

@Service
public class ManualTransactionService {

  @Autowired
  private OrderMapper orderMapper;

  @Autowired
  private StockMapper stockMapper;

  public void createOrder(Order order, String productId, int quantity) {
    try {
      // 开启事务
      StartTransactionAction startTransactionAction = new StartTransactionAction("order_create");
      TransactionInfo transactionInfo = startTransactionAction.start();

      // 创建订单
      orderMapper.insert(order);

      // 扣减库存
      int affectedRows = stockMapper.decreaseStock(productId, quantity);
      if (affectedRows < 1) {
        throw new RuntimeException("库存扣减失败");
      }

      // 提交事务
      CommitTransactionAction commitTransactionAction = new CommitTransactionAction(transactionInfo.getXid());
      commitTransactionAction.commit();

    } catch (Exception e) {
      // 回滚事务
      RollbackTransactionAction rollbackTransactionAction = new RollbackTransactionAction(transactionInfo.getXid());
      rollbackTransactionAction.rollback();
      throw e;
    }
  }
}

手动提交事务

@Service
public class ManualTransactionService {

  @Autowired
  private OrderMapper orderMapper;

  @Autowired
  private StockMapper stockMapper;

  public void createOrder(Order order, String productId, int quantity) {
    try {
      // 开启事务
      StartTransactionAction startTransactionAction = new StartTransactionAction("order_create");
      TransactionInfo transactionInfo = startTransactionAction.start();

      // 创建订单
      orderMapper.insert(order);

      // 扣减库存
      int affectedRows = stockMapper.decreaseStock(productId, quantity);
      if (affectedRows < 1) {
        throw new RuntimeException("库存扣减失败");
      }

      // 提交事务
      CommitTransactionAction commitTransactionAction = new CommitTransactionAction(transactionInfo.getXid());
      commitTransactionAction.commit();

    } catch (Exception e) {
      // 回滚事务
      RollbackTransactionAction rollbackTransactionAction = new RollbackTransactionAction(transactionInfo.getXid());
      rollbackTransactionAction.rollback();
      throw e;
    }
  }
}

手动回滚事务

@Service
public class ManualTransactionService {

  @Autowired
  private OrderMapper orderMapper;

  @Autowired
  private StockMapper stockMapper;

  public void createOrder(Order order, String productId, int quantity) {
    try {
      // 开启事务
      StartTransactionAction startTransactionAction = new StartTransactionAction("order_create");
      TransactionInfo transactionInfo = startTransactionAction.start();

      // 创建订单
      orderMapper.insert(order);

      // 扣减库存
      int affectedRows = stockMapper.decreaseStock(productId, quantity);
      if (affectedRows < 1) {
        throw new RuntimeException("库存扣减失败");
      }

      // 提交事务
      CommitTransactionAction commitTransactionAction = new CommitTransactionAction(transactionInfo.getXid());
      commitTransactionAction.commit();

    } catch (Exception e) {
      // 回滚事务
      RollbackTransactionAction rollbackTransactionAction = new RollbackTransactionAction(transactionInfo.getXid());
      rollbackTransactionAction.rollback();
      throw e;
    }
  }
}

通过这种方式,可以精确控制每个步骤的执行,确保分布式事务的一致性。

进行事务回滚与补偿操作

在某些情况下,事务在提交阶段可能会失败。此时需要进行事务回滚和补偿操作。

事务回滚

@Service
public class ManualTransactionService {

  @Autowired
  private OrderMapper orderMapper;

  @Autowired
  private StockMapper stockMapper;

  public void createOrder(Order order, String productId, int quantity) {
    try {
      // 开启事务
      StartTransactionAction startTransactionAction = new StartTransactionAction("order_create");
      TransactionInfo transactionInfo = startTransactionAction.start();

      // 创建订单
      orderMapper.insert(order);

      // 扣减库存
      int affectedRows = stockMapper.decreaseStock(productId, quantity);
      if (affectedRows < 1) {
        throw new RuntimeException("库存扣减失败");
      }

      // 提交事务
      CommitTransactionAction commitTransactionAction = new CommitTransactionAction(transactionInfo.getXid());
      commitTransactionAction.commit();

    } catch (Exception e) {
      // 回滚事务
      RollbackTransactionAction rollbackTransactionAction = new RollbackTransactionAction(transactionInfo.getXid());
      rollbackTransactionAction.rollback();
      throw e;
    }
  }
}

事务补偿

补偿操作通常在事务回滚后执行,以确保系统处于一致状态。

@Service
public class ManualTransactionService {

  @Autowired
  private OrderMapper orderMapper;

  @Autowired
  private StockMapper stockMapper;

  public void createOrder(Order order, String productId, int quantity) {
    try {
      // 开启事务
      StartTransactionAction startTransactionAction = new StartTransactionAction("order_create");
      TransactionInfo transactionInfo = startTransactionAction.start();

      // 创建订单
      orderMapper.insert(order);

      // 扣减库存
      int affectedRows = stockMapper.decreaseStock(productId, quantity);
      if (affectedRows < 1) {
        throw new RuntimeException("库存扣减失败");
      }

      // 提交事务
      CommitTransactionAction commitTransactionAction = new CommitTransactionAction(transactionInfo.getXid());
      commitTransactionAction.commit();

    } catch (Exception e) {
      // 回滚事务
      RollbackTransactionAction rollbackTransactionAction = new RollbackTransactionAction(transactionInfo.getXid());
      rollbackTransactionAction.rollback();

      // 补偿操作
      if (e instanceof RuntimeException) {
        try {
          // 补偿操作,例如回滚订单或回滚库存
          // orderService.cancelOrder(order);
          // stockService.cancelDecreaseStock(productId, quantity);
        } catch (Exception e1) {
          // 日志记录
        }
      }
      throw e;
    }
  }
}

通过手动编排和补偿操作,可以确保分布式事务在异常情况下的数据一致性。

Seata和Mysql项目总结与实战技巧
项目总结与优化建议

在实际项目中使用Seata和MySQL可以带来以下几点好处:

  1. 一致性保证:确保分布式系统中的多个服务操作的一致性。
  2. 高可用性:Seata的设计确保了系统在服务失效或其他异常情况下的回滚和补偿操作。
  3. 性能优化:通过配置合理的超时时间和隔离级别,可以优化分布式事务的执行性能。

优化建议

  1. 性能优化

    • 减少事务范围:尽量减少事务中的操作范围,避免不必要的数据库操作。
    • 资源锁定:合理使用资源锁定机制,减少并发冲突。
    • 超时时间:合理设置事务的超时时间,避免分布式事务长时间等待。
  2. 故障恢复

    • 补偿机制:在事务回滚后,提供补偿操作以恢复系统的状态。
    • 日志记录:详细的日志记录可以帮助定位问题和优化系统性能。
  3. 配置优化
    • 配置优化:根据实际业务需求,调整Seata的配置参数,如隔离级别、超时时间等。

常见问题

  1. 事务回滚失败

    • 原因:事务回滚失败可能是因为某个服务挂起或响应超时。
    • 解决:检查服务端和客户端的日志,确认问题原因。
  2. 事务提交失败

    • 原因:事务提交失败可能是因为数据库连接异常或资源竞争。
    • 解决:检查数据库连接和资源状态,确保事务提交前的资源状态正常。
  3. 性能瓶颈
    • 原因:性能瓶颈可能是因为数据库操作频繁或资源竞争严重。
    • 解决:优化数据库操作,减少事务范围,合理配置资源。

调试技巧

  1. 日志分析

    • 使用Seata自带的日志工具分析系统日志,定位问题原因。
  2. 模拟异常

    • 通过模拟异常情况来测试系统的故障恢复机制。
  3. 性能监控
    • 使用性能监控工具,如Prometheus和Grafana,监控系统的运行状态。

通过以上总结和优化建议,可以在实际项目中更好地使用Seata和MySQL,确保系统的稳定性和可靠性。

总结

本教程详细介绍了Seata的基本概念、安装配置、与MySQL的集成示例、分布式事务的模拟和处理方法。通过实践示例和技巧分享,希望读者能够掌握Seata和MySQL在实际项目中的应用。更多关于Seata的详细信息和最佳实践,可以参考Seata的官方文档和社区讨论。

点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消