1. 环境准备
确保以下环境已经安装并正常运行:
- JDK 1.8
- MySQL(或其他支持的数据库)
- Nacos Server(用于注册中心和配置中心)
- Seata Server
2. 配置 Seata Server
(1) 下载并解压 Seata
从 Seata 官方 GitHub 下载最新版本的 Seata,并解压到你的服务器上。
(2) 初始化 Seata 数据库表
在 MySQL 中创建一个数据库(如 seata),并执行 Seata 提供的 SQL 脚本 db_store.sql,初始化以下表:
global_tablebranch_tablelock_table
示例脚本路径:seata/conf/db_store.sql
CREATE DATABASE seata;
USE seata;
-- global_table
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`)
);
-- branch_table
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,
`gmt_modified` DATETIME,
PRIMARY KEY (`branch_id`),
KEY `idx_xid` (`xid`)
);
-- lock_table
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`)
);
(3) 修改 registry.conf
编辑 seata/conf/registry.conf 文件,将注册中心和配置中心设置为 Nacos。
registry {
type = "nacos"
nacos {
application = "seata-server"
serverAddr = "127.0.0.1:8848"
group = "SEATA_GROUP"
namespace = ""
}
}
config {
type = "nacos"
nacos {
serverAddr = "127.0.0.1:8848"
group = "SEATA_GROUP"
namespace = ""
dataId = "seataServer.properties"
}
}
(4) 修改 file.conf
编辑 seata/conf/file.conf 文件,将存储模式设置为 db,并配置数据库连接信息。
store {
mode = "db"
db {
datasource = "druid"
dbType = "mysql"
driverClassName = "com.mysql.cj.jdbc.Driver"
url = "jdbc:mysql://127.0.0.1:3306/seata?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=UTC"
user = "root"
password = "password"
}
}
(5) 启动 Seata Server
运行以下命令启动 Seata Server:
sh seata-server.sh -p 8091 -m db
-p:指定 Seata Server 的端口号,默认是 8091。-m:指定存储模式,这里设置为db。
3. 配置微服务
(1) 引入依赖
在每个微服务的 pom.xml 文件中添加 Seata 和 Spring Cloud 相关依赖。
<dependencies>
<!-- Spring Boot Starter -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Spring Cloud Alibaba Seata Starter -->
<dependency>
<groupId>io.seata</groupId>
<artifactId>seata-spring-boot-starter</artifactId>
<version>1.6.0</version> <!-- 请根据实际版本号调整 -->
</dependency>
<!-- Spring Cloud Alibaba Nacos Discovery -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
<version>2022.0.0.0</version> <!-- 请根据实际版本号调整 -->
</dependency>
<!-- 数据库相关依赖 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.0</version> <!-- 请根据实际版本号调整 -->
</dependency>
</dependencies>
(2) 创建 UNDO_LOG 表
在每个微服务使用的数据库中,创建 UNDO_LOG 表。这是 AT 模式所需的回滚日志表。
CREATE TABLE IF NOT EXISTS `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,
PRIMARY KEY (`id`),
UNIQUE KEY `ux_undo_log` (`xid`, `branch_id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
(3) 配置 application.yml
在每个微服务的 application.yml 文件中,配置 Seata、Nacos 和数据库连接信息。
spring:
application:
name: order-service # 微服务名称
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848 # Nacos 服务器地址
datasource:
url: jdbc:mysql://127.0.0.1:3306/order_db?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=UTC
username: root
password: password
driver-class-name: com.mysql.cj.jdbc.Driver
seata:
enabled: true
tx-service-group: my_tx_group # 自定义事务组名称
service:
vgroup-mapping:
my_tx_group: default # 映射到 Seata Server 的事务组
grouplist:
default: 127.0.0.1:8091 # Seata Server 地址
registry:
type: nacos
nacos:
application: seata-server
server-addr: 127.0.0.1:8848
group: SEATA_GROUP
config:
type: nacos
nacos:
server-addr: 127.0.0.1:8848
group: SEATA_GROUP
data-source-proxy-mode: AT # 数据源代理模式,支持 AT、XA,默认为 AT
(4) 数据源代理
为了让 Seata 拦截业务 SQL 并生成回滚日志,需要对数据源进行代理。Spring Boot 项目中可以通过 @EnableAutoDataSourceProxy 注解自动完成代理。
主类示例:
@SpringBootApplication
@EnableAutoDataSourceProxy
public class OrderServiceApplication {
public static void main(String[] args) {
SpringApplication.run(OrderServiceApplication.class, args);
}
}
(5) 开启全局事务
在需要使用分布式事务的方法上,添加 @GlobalTransactional 注解。
@Service
public class OrderService {
@Autowired
private OrderMapper orderMapper;
@Autowired
private InventoryService inventoryService;
@GlobalTransactional
public void createOrder(Order order) {
// 创建订单
orderMapper.insert(order);
// 扣减库存
inventoryService.reduceStock(order.getProductId(), order.getCount());
}
}
4. 测试与验证
- 启动 Nacos Server。
- 启动 Seata Server。
- 启动所有微服务。
- 测试一个包含多个微服务调用的分布式事务场景,确保事务能够正确提交或回滚。
总结
通过以上步骤,你可以成功地在 Spring Boot、Spring Cloud 和 Nacos 环境下集成 Seata 的 DB 模式,实现分布式事务管理。关键点包括:
- 配置 Seata Server 并初始化事务日志表。
- 在微服务中引入 Seata 依赖,创建
UNDO_LOG表。 - 使用
application.yml配置 Seata、Nacos 和数据库连接信息。 - 使用
@GlobalTransactional注解开启全局事务。
这样可以确保你的微服务架构具备强大的分布式事务管理能力,同时保持代码的简洁性和可维护性。
几种模式的对比以及运用
1. AT 模式 vs TCC 模式
| 特性 | AT 模式 | TCC 模式 |
|---|---|---|
| 数据源代理 | 需要数据源代理(@EnableAutoDataSourceProxy) |
不需要数据源代理 |
| 回滚机制 | 自动通过 UNDO_LOG 表生成回滚日志 |
手动实现 Try、Confirm 和 Cancel 方法 |
| 适用场景 | 适用于大多数 CRUD 场景 | 适用于复杂业务场景,要求更高的灵活性 |
| 配置复杂度 | 简单,仅需创建 UNDO_LOG 表 |
复杂,需手动编写补偿逻辑 |
2. 使用 TCC 模式的配置与代码调整
(1) 修改 data-source-proxy-mode
在 application.yml 中,将数据源代理模式设置为 none,因为 TCC 模式不需要数据源代理。
seata:
data-source-proxy-mode: none # TCC 模式不使用数据源代理
(2) 编写 TCC 接口
TCC 模式要求开发者手动实现三个阶段的方法:Try、Confirm 和 Cancel。
示例:
@LocalTCC
public interface AccountService {
/**
* Try 阶段:预留资源
*/
@TwoPhaseBusinessAction(name = "accountTccAction", commitMethod = "confirm", rollbackMethod = "cancel")
boolean tryFreeze(@BusinessActionContextParameter(paramName = "userId") String userId,
@BusinessActionContextParameter(paramName = "amount") BigDecimal amount);
/**
* Confirm 阶段:确认操作
*/
boolean confirm(BusinessActionContext context);
/**
* Cancel 阶段:取消操作
*/
boolean cancel(BusinessActionContext context);
}
(3) 实现 TCC 方法
实现上述接口中的三个方法。
示例:
@Service
public class AccountServiceImpl implements AccountService {
@Override
public boolean tryFreeze(String userId, BigDecimal amount) {
// 尝试冻结用户的资金
System.out.println("Try to freeze account: " + userId + ", amount: " + amount);
return true; // 返回是否成功
}
@Override
public boolean confirm(BusinessActionContext context) {
// 确认扣款
String userId = (String) context.getActionContext("userId");
BigDecimal amount = (BigDecimal) context.getActionContext("amount");
System.out.println("Confirm account: " + userId + ", amount: " + amount);
return true;
}
@Override
public boolean cancel(BusinessActionContext context) {
// 取消冻结
String userId = (String) context.getActionContext("userId");
BigDecimal amount = (BigDecimal) context.getActionContext("amount");
System.out.println("Cancel account: " + userId + ", amount: " + amount);
return true;
}
}
(4) 开启全局事务
在调用 TCC 方法时,仍然需要使用 @GlobalTransactional 注解开启全局事务。
示例:
@Service
public class OrderService {
@Autowired
private AccountService accountService;
@GlobalTransactional
public void createOrder(Order order) {
// 调用 TCC 方法
boolean result = accountService.tryFreeze(order.getUserId(), order.getAmount());
if (!result) {
throw new RuntimeException("Failed to freeze account");
}
// 其他业务逻辑
}
}
3. 使用 SAGA 模式的配置与代码调整
SAGA 模式是一种基于状态机的分布式事务管理方式,适用于长事务场景。
(1) 配置 Saga 模式
在 application.yml 中,无需特别指定模式,因为 Saga 模式是通过状态机 DSL 文件定义的。
(2) 创建状态机 DSL 文件
Saga 模式需要一个状态机描述文件(JSON 格式),定义事务的各个阶段和补偿逻辑。
示例:
{
"name": "orderSaga",
"version": "1.0.0",
"startState": "createOrder",
"states": {
"createOrder": {
"type": "ServiceTask",
"serviceMethod": "orderService.createOrder",
"compensateState": "cancelOrder"
},
"cancelOrder": {
"type": "Compensation",
"serviceMethod": "orderService.cancelOrder"
}
}
}
(3) 引入 Saga Starter
在微服务中引入 Saga Starter 依赖。
<dependency>
<groupId>io.seata</groupId>
<artifactId>seata-saga-starter</artifactId>
<version>1.6.0</version>
</dependency>
(4) 启动 Saga 状态机
通过 Saga 状态机启动事务。
@Autowired
private StateMachineEngine stateMachineEngine;
public void startSaga() {
Map<String, Object> params = new HashMap<>();
params.put("userId", "123");
params.put("amount", 100);
stateMachineEngine.start("orderSaga", null, params);
}
4. 使用 XA 模式的配置与代码调整
XA 模式直接依赖数据库的 XA 协议实现分布式事务。
(1) 修改 data-source-proxy-mode
在 application.yml 中,将数据源代理模式设置为 XA。
seata:
data-source-proxy-mode: XA
(2) 数据库支持
确保你的数据库支持 XA 协议(如 MySQL、Oracle 等)。
(3) 开启全局事务
与其他模式相同,仍然需要使用 @GlobalTransactional 注解。
@Service
public class OrderService {
@GlobalTransactional
public void createOrder(Order order) {
// 业务逻辑
}
}
5. 总结
- AT 模式:简单易用,适合大多数 CRUD 场景,自动管理回滚日志。
- TCC 模式:灵活但复杂,需要手动实现补偿逻辑,适合复杂业务场景。
- SAGA 模式:基于状态机,适合长事务场景。
- XA 模式:依赖数据库的 XA 协议,性能较低,适合对一致性要求极高的场景。
根据你的业务需求选择合适的模式,并按照上述步骤进行配置和代码调整。