本文详细介绍了Seata与MySQL存储的集成,包括Seata的基本概念、服务端部署、连接配置以及与Spring Boot项目的整合。通过具体示例演示了如何使用Seata管理分布式事务,确保数据的一致性和可靠性。文章还提供了手动编排事务流程和进行事务回滚与补偿操作的实战代码示例。本文旨在帮助读者掌握Seata和MySQL存储演示项目实战。
Seata简介与基本概念 什么是SeataSeata(Software Transaction Access Layer)是一个开源的分布式事务解决方案,旨在帮助用户在微服务架构中实现高性能的分布式事务管理。Seata支持XA、TCC、SAGA、可靠消息等分布式事务模式,但最常用的是TCC模式。通过Seata,开发人员可以在微服务架构中轻松地实现分布式事务的管理和控制,确保数据的一致性和可靠性。
Seata的核心组件介绍Seata的核心组件包括以下几个部分:
-
Transaction Service:负责分布式事务的全局管理和协调。它管理来自不同应用的服务注册和事务的提交或回滚请求。
-
Transaction Log:存储全局事务状态的数据库,用于持久化存储全局事务的执行状态。Seata默认使用MySQL、Oracle、TiDB等作为存储,也可使用文件或Redis等其他存储方式。
-
Transaction Manager:提供分布式事务的全局协调服务,包括事务的注册、提交、回滚等操作。
-
Resource Service:与应用中的数据源绑定,执行本地事务提交或回滚。Seata通过代理类和切面增强技术,在本地数据库操作中插入事务控制逻辑,从而实现对分布式事务的管理。
- Transaction Coordinator:协调全局事务的执行,根据全局事务的执行情况,决定事务的最终状态并通知各个局部事务执行相应操作。
Seata的工作流程如下:
-
全局事务开启:服务端接收到客户端的全局事务开启请求后,将全局事务注册到事务管理器中,并返回一个全局事务标识(XID)给客户端。
-
本地操作:客户端发起数据库操作,业务逻辑中调用Seata提供的代理类或切面增强方法,执行本地事务操作。
-
本地事务提交:当本地事务执行完成后,Seata会封装一个本地事务提交请求,发送给事务管理器。
-
提交/回滚决定:事务管理器根据各个局部事务的状态,决定全局事务的最终状态(提交或回滚),并将决定发送给各个局部事务服务。
-
局部事务执行:各局部事务服务根据事务管理器的决定执行提交或回滚操作。
- 事务状态持久化:事务状态信息会被持久化到Seata的存储模块中,以便后续的事务状态查询和回查操作。
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.conf
和conf/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数据库。
创建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
数据库中的表来验证。
创建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是否正确管理了分布式事务。
配置全局事务
在服务类中使用@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在实际项目中的应用,我们来模拟一个订单系统中的分布式事务场景。
场景描述
在一个典型的电商系统中,订单和库存管理通常分布在不同的服务中,需要保证订单创建和库存扣减操作的一致性。具体场景如下:
- 订单创建:当用户下单时,需要创建一个订单记录。
- 库存扣减:创建订单后,需要扣减对应的库存数量。
项目结构
项目结构如下:
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可以带来以下几点好处:
- 一致性保证:确保分布式系统中的多个服务操作的一致性。
- 高可用性:Seata的设计确保了系统在服务失效或其他异常情况下的回滚和补偿操作。
- 性能优化:通过配置合理的超时时间和隔离级别,可以优化分布式事务的执行性能。
优化建议
-
性能优化:
- 减少事务范围:尽量减少事务中的操作范围,避免不必要的数据库操作。
- 资源锁定:合理使用资源锁定机制,减少并发冲突。
- 超时时间:合理设置事务的超时时间,避免分布式事务长时间等待。
-
故障恢复:
- 补偿机制:在事务回滚后,提供补偿操作以恢复系统的状态。
- 日志记录:详细的日志记录可以帮助定位问题和优化系统性能。
- 配置优化:
- 配置优化:根据实际业务需求,调整Seata的配置参数,如隔离级别、超时时间等。
常见问题
-
事务回滚失败:
- 原因:事务回滚失败可能是因为某个服务挂起或响应超时。
- 解决:检查服务端和客户端的日志,确认问题原因。
-
事务提交失败:
- 原因:事务提交失败可能是因为数据库连接异常或资源竞争。
- 解决:检查数据库连接和资源状态,确保事务提交前的资源状态正常。
- 性能瓶颈:
- 原因:性能瓶颈可能是因为数据库操作频繁或资源竞争严重。
- 解决:优化数据库操作,减少事务范围,合理配置资源。
调试技巧
-
日志分析:
- 使用Seata自带的日志工具分析系统日志,定位问题原因。
-
模拟异常:
- 通过模拟异常情况来测试系统的故障恢复机制。
- 性能监控:
- 使用性能监控工具,如Prometheus和Grafana,监控系统的运行状态。
通过以上总结和优化建议,可以在实际项目中更好地使用Seata和MySQL,确保系统的稳定性和可靠性。
总结本教程详细介绍了Seata的基本概念、安装配置、与MySQL的集成示例、分布式事务的模拟和处理方法。通过实践示例和技巧分享,希望读者能够掌握Seata和MySQL在实际项目中的应用。更多关于Seata的详细信息和最佳实践,可以参考Seata的官方文档和社区讨论。
共同学习,写下你的评论
评论加载中...
作者其他优质文章