Seata入门教程 - 分布式事务解决方案完整指南
Seata入门教程 - 从零开始学习分布式事务解决方案
目录
- Seata简介
- 环境搭建
- 核心概念
- AT模式详解
- TCC模式详解
- Saga模式详解
- Seata与Spring Cloud集成
- Seata与Spring Boot集成
- 配置详解
- 实际应用案例
- 常见问题与解决方案
- 最佳实践
- 总结与进阶
1. Seata简介
Seata(Simple Extensible Autonomous Transaction Architecture)是阿里巴巴开源的分布式事务解决方案,致力于提供高性能和简单易用的分布式事务服务。
Seata的核心特点:
- ✅ 高性能:零侵入,高性能
- ✅ 易用性:简单易用,学习成本低
- ✅ 多模式:支持AT、TCC、Saga、XA等多种事务模式
- ✅ 生态完善:与Spring Cloud、Dubbo等框架深度集成
- ✅ 高可用:支持集群部署,高可用
在微服务架构中,一个业务操作可能涉及多个服务,每个服务都有自己的数据库。传统的本地事务无法保证跨服务的数据一致性,这就是分布式事务问题。
典型场景:
用户下单 → 订单服务(创建订单) → 库存服务(扣减库存) → 账户服务(扣款)如果任何一个服务失败,都需要回滚所有操作,保证数据一致性。
Seata包含三个核心组件:
- TC (Transaction Coordinator):事务协调器,维护全局事务的运行状态
- TM (Transaction Manager):事务管理器,开启全局事务
- RM (Resource Manager):资源管理器,管理分支事务
架构图:
TM TC RM
| | |
|---开启全局事务----->| |
| | |
| |---注册分支事务----->|
| | |
| |---提交/回滚-------->|AT模式:自动补偿模式,零侵入,高性能(推荐)
TCC模式:Try-Confirm-Cancel模式,需要业务实现
Saga模式:长事务解决方案
XA模式:XA协议实现
微服务架构下的分布式事务
多数据源事务管理
跨服务的数据一致性保证
电商、金融等对一致性要求高的场景
2. 环境搭建
- JDK 8或更高版本
- Maven 3.6+
- MySQL 5.7+(用于存储事务日志)
- Nacos/Consul/Eureka(注册中心,可选)
- Redis(用于存储会话,可选)
方式一:从GitHub下载
# 访问 https://github.com/seata/seata/releases
# 下载最新版本的seata-server压缩包
wget https://github.com/seata/seata/releases/download/v1.7.1/seata-server-1.7.1.zip
unzip seata-server-1.7.1.zip
cd seata方式二:使用Docker
docker pull seataio/seata-server:latest创建Seata所需的数据库表:
-- 创建数据库
CREATE DATABASE IF NOT EXISTS seata DEFAULT CHARACTER SET utf8mb4;
USE seata;
-- 全局事务表
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=InnoDB DEFAULT CHARSET=utf8mb4;
-- 分支事务表
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=InnoDB DEFAULT CHARSET=utf8mb4;
-- 锁表
CREATE TABLE IF NOT EXISTS lock_table (
row_key VARCHAR(128) NOT NULL,
xid VARCHAR(128),
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=InnoDB DEFAULT CHARSET=utf8mb4;修改conf/application.yml
server:
port: 7091
spring:
application:
name: seata-server
logging:
config: classpath:logback-spring.xml
file:
path: ${user.home}/logs/seata
console:
user:
username: seata
password: seata
seata:
config:
# 配置文件类型:file、nacos、consul、apollo、zk、etcd3
type: file
file:
name: file.conf
registry:
# 注册中心类型:file、nacos、eureka、redis、zk、consul、etcd3、sofa
type: file
file:
name: file.conf
store:
# 存储模式:file、db、redis
mode: db
db:
datasource: druid
db-type: mysql
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/seata?rewriteBatchedStatements=true
user: root
password: root
min-conn: 10
max-conn: 100
global-table: global_table
branch-table: branch_table
lock-table: lock_table
distributed-lock-table: distributed_lock修改conf/file.conf(如果使用file模式)
## transaction log store, only used in seata-server
store {
## store mode: file、db、redis
mode = "db"
## database store property
db {
datasource = "druid"
dbType = "mysql"
driverClassName = "com.mysql.cj.jdbc.Driver"
url = "jdbc:mysql://127.0.0.1:3306/seata?rewriteBatchedStatements=true"
user = "root"
password = "root"
minConn = 10
maxConn = 100
globalTable = "global_table"
branchTable = "branch_table"
lockTable = "lock_table"
queryLimit = 100
maxWait = 5000
}
}方式一:直接启动
cd seata/bin
# Linux/Mac
sh seata-server.sh
# Windows
seata-server.bat方式二:使用Docker
docker run -d --name seata-server \
-p 8091:8091 \
-p 7091:7091 \
-e SEATA_CONFIG_NAME=file:/root/seata-config/registry \
-v /path/to/conf:/root/seata-config \
seataio/seata-server:latest验证启动:
访问 http://localhost:7091 查看Seata控制台(默认用户名/密码:seata/seata)
创建Maven父项目:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>seata-demo</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>pom</packaging>
<modules>
<module>order-service</module>
<module>storage-service</module>
<module>account-service</module>
</modules>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<spring-boot.version>2.7.14</spring-boot.version>
<spring-cloud.version>2021.0.8</spring-cloud.version>
<seata.version>1.7.1</seata.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>2021.0.5.0</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
</project>3. 核心概念
TC (Transaction Coordinator) - 事务协调器
- 维护全局事务和分支事务的状态
- 驱动全局事务的提交或回滚
- Seata Server就是TC
TM (Transaction Manager) - 事务管理器
- 定义全局事务的边界
- 开启全局事务
- 提交或回滚全局事务
RM (Resource Manager) - 资源管理器
- 管理分支事务的资源
- 向TC注册分支事务
- 报告分支事务状态
- 驱动分支事务的提交或回滚
正常流程:
1. TM开启全局事务,向TC注册全局事务记录
2. RM向TC注册分支事务
3. RM向TC报告分支事务状态
4. TM向TC发起全局提交或回滚
5. TC驱动所有RM完成分支事务的提交或回滚异常流程:
1. 如果任何RM报告分支事务失败
2. TM向TC发起全局回滚
3. TC驱动所有RM完成分支事务的回滚XID是全局事务的唯一标识,在事务传播过程中,XID会在服务间传递。
4. AT模式详解
AT模式是Seata推荐的模式,具有以下特点:
- 零侵入:业务代码无需改造
- 自动补偿:自动生成回滚SQL
- 高性能:基于本地ACID事务
工作原理:
- 业务SQL执行前,Seata拦截SQL,解析语义,保存"前镜像"
- 执行业务SQL
- 业务SQL执行后,保存"后镜像",生成行锁
- 提交前,向TC注册分支事务
- 本地事务提交
- 向TC报告分支事务状态
回滚机制:
- 收到TC回滚请求
- 开启本地事务
- 通过XID和Branch ID找到回滚日志
- 根据回滚日志生成反向SQL并执行
- 提交本地事务
- 向TC报告回滚结果
添加依赖
在子模块的pom.xml中添加:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-seata</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.2.16</version>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.3.1</version>
</dependency>
</dependencies>创建undo_log表
在每个业务数据库中创建undo_log表:
CREATE TABLE IF NOT EXISTS undo_log (
branch_id BIGINT NOT NULL COMMENT 'branch transaction id',
xid VARCHAR(128) 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 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=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='AT transaction mode undo table';配置Seata
创建application.yml:
server:
port: 8081
spring:
application:
name: order-service
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/order_db?useUnicode=true&characterEncoding=utf-8&useSSL=false
username: root
password: root
cloud:
nacos:
discovery:
server-addr: localhost:8848
alibaba:
seata:
tx-service-group: my_test_tx_group
seata:
enabled: true
application-id: ${spring.application.name}
tx-service-group: my_test_tx_group
config:
type: nacos
nacos:
server-addr: localhost:8848
namespace: ""
group: SEATA_GROUP
data-id: seataServer.properties
registry:
type: nacos
nacos:
application: seata-server
server-addr: localhost:8848
namespace: ""
group: SEATA_GROUP
mybatis:
mapper-locations: classpath:mapper/*.xml
type-aliases-package: com.example.entity创建实体类
package com.example.entity;
import java.math.BigDecimal;
import java.time.LocalDateTime;
public class Order {
private Long id;
private String userId;
private String commodityCode;
private Integer count;
private BigDecimal money;
private LocalDateTime createTime;
// getter/setter...
}创建Mapper
package com.example.mapper;
import com.example.entity.Order;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface OrderMapper {
void insert(Order order);
void updateStatus(Long id, String status);
}<!-- OrderMapper.xml -->
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.mapper.OrderMapper">
<insert id="insert" parameterType="Order">
INSERT INTO t_order (user_id, commodity_code, count, money, status)
VALUES (#{userId}, #{commodityCode}, #{count}, #{money}, 'CREATING')
</insert>
<update id="updateStatus">
UPDATE t_order SET status = #{status} WHERE id = #{id}
</update>
</mapper>创建Service
package com.example.service;
import com.example.entity.Order;
import com.example.mapper.OrderMapper;
import io.seata.spring.annotation.GlobalTransactional;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class OrderService {
@Autowired
private OrderMapper orderMapper;
@Autowired
private StorageService storageService;
@Autowired
private AccountService accountService;
/**
* 创建订单
* @GlobalTransactional 开启全局事务
*/
@GlobalTransactional(rollbackFor = Exception.class)
public void create(Order order) {
// 1. 创建订单
orderMapper.insert(order);
// 2. 扣减库存
storageService.deduct(order.getCommodityCode(), order.getCount());
// 3. 扣减账户余额
accountService.debit(order.getUserId(), order.getMoney());
// 4. 更新订单状态
orderMapper.updateStatus(order.getId(), "SUCCESS");
}
}创建Controller
package com.example.controller;
import com.example.entity.Order;
import com.example.service.OrderService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/order")
public class OrderController {
@Autowired
private OrderService orderService;
@PostMapping("/create")
public String create(@RequestBody Order order) {
try {
orderService.create(order);
return "订单创建成功";
} catch (Exception e) {
return "订单创建失败: " + e.getMessage();
}
}
}- 必须使用数据源代理:Seata会自动代理数据源
- undo_log表必须创建:用于存储回滚日志
- @GlobalTransactional注解:在事务入口方法上使用
- 异常传播:确保异常能够传播到TM
5. TCC模式详解
TCC模式需要业务实现三个操作:
- Try:尝试执行业务,完成所有业务检查,预留业务资源
- Confirm:确认执行业务,真正执行业务,不做业务检查
- Cancel:取消执行业务,释放Try阶段预留的业务资源
定义TCC接口
package com.example.tcc;
import io.seata.rm.tcc.api.BusinessActionContext;
import io.seata.rm.tcc.api.BusinessActionContextParameter;
import io.seata.rm.tcc.api.LocalTCC;
import io.seata.rm.tcc.api.TwoPhaseBusinessAction;
@LocalTCC
public interface AccountTccAction {
@TwoPhaseBusinessAction(
name = "accountTccAction",
commitMethod = "commit",
rollbackMethod = "cancel"
)
boolean tryDeduct(
@BusinessActionContextParameter(paramName = "userId") String userId,
@BusinessActionContextParameter(paramName = "money") BigDecimal money,
BusinessActionContext context
);
boolean commit(BusinessActionContext context);
boolean cancel(BusinessActionContext context);
}实现TCC接口
package com.example.tcc.impl;
import com.example.tcc.AccountTccAction;
import com.example.mapper.AccountMapper;
import io.seata.rm.tcc.api.BusinessActionContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class AccountTccActionImpl implements AccountTccAction {
@Autowired
private AccountMapper accountMapper;
@Override
public boolean tryDeduct(String userId, BigDecimal money, BusinessActionContext context) {
// Try阶段:冻结金额
int result = accountMapper.freeze(userId, money);
return result > 0;
}
@Override
public boolean commit(BusinessActionContext context) {
// Confirm阶段:扣减冻结金额
String userId = (String) context.getActionContext("userId");
BigDecimal money = (BigDecimal) context.getActionContext("money");
accountMapper.deductFreeze(userId, money);
return true;
}
@Override
public boolean cancel(BusinessActionContext context) {
// Cancel阶段:释放冻结金额
String userId = (String) context.getActionContext("userId");
BigDecimal money = (BigDecimal) context.getActionContext("money");
accountMapper.unfreeze(userId, money);
return true;
}
}使用TCC
@Service
public class OrderService {
@Autowired
private AccountTccAction accountTccAction;
@GlobalTransactional(rollbackFor = Exception.class)
public void create(Order order) {
// 调用TCC
accountTccAction.tryDeduct(order.getUserId(), order.getMoney(), null);
// 其他业务逻辑...
}
}- 幂等性:Try、Confirm、Cancel必须保证幂等性
- 空回滚:Cancel可能在没有Try的情况下被调用
- 悬挂:Cancel在Try之后执行
- 资源预留:Try阶段只预留资源,不真正执行业务
6. Saga模式详解
Saga模式适用于长事务场景,将长事务拆分为多个本地事务,每个本地事务都有对应的补偿操作。
定义Saga接口
package com.example.saga;
import io.seata.saga.engine.annotation.EnableSaga;
import io.seata.saga.tm.annotation.SagaTransactional;
public interface OrderSagaService {
@SagaTransactional
void createOrder(Order order);
}7. Seata与Spring Cloud集成
在Nacos中创建配置:
Data ID: seataServer.properties
Group: SEATA_GROUP
store.mode=db
store.db.datasource=druid
store.db.dbType=mysql
store.db.driverClassName=com.mysql.cj.jdbc.Driver
store.db.url=jdbc:mysql://127.0.0.1:3306/seata?rewriteBatchedStatements=true
store.db.user=root
store.db.password=root
store.db.minConn=10
store.db.maxConn=100
store.db.globalTable=global_table
store.db.branchTable=branch_table
store.db.lockTable=lock_table使用Feign或RestTemplate进行服务间调用时,Seata会自动传播XID。
@FeignClient(name = "storage-service")
public interface StorageService {
@PostMapping("/storage/deduct")
void deduct(@RequestParam("commodityCode") String commodityCode,
@RequestParam("count") Integer count);
}8. Seata与Spring Boot集成
spring:
cloud:
alibaba:
seata:
tx-service-group: my_test_tx_group
enabled: true@SpringBootApplication
@EnableDiscoveryClient
public class OrderApplication {
public static void main(String[] args) {
SpringApplication.run(OrderApplication.class, args);
}
}9. 配置详解
File模式(不推荐生产环境)
seata:
store:
mode: fileDB模式(推荐)
seata:
store:
mode: db
db:
datasource: druid
db-type: mysql
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/seata
user: root
password: rootRedis模式
seata:
store:
mode: redis
redis:
host: 127.0.0.1
port: 6379
database: 0支持Nacos、Eureka、Consul、Zookeeper等。
seata:
registry:
type: nacos
nacos:
server-addr: localhost:8848
namespace: ""
group: SEATA_GROUPseata:
config:
type: nacos
nacos:
server-addr: localhost:8848
namespace: ""
group: SEATA_GROUP
data-id: seataServer.properties10. 实际应用案例
业务场景:
用户下单 → 创建订单 → 扣减库存 → 扣减账户余额
实现步骤:
- 订单服务
@Service
public class OrderService {
@GlobalTransactional
public void createOrder(Order order) {
orderMapper.insert(order);
storageService.deduct(order.getCommodityCode(), order.getCount());
accountService.debit(order.getUserId(), order.getMoney());
}
}- 库存服务
@Service
public class StorageService {
public void deduct(String commodityCode, Integer count) {
storageMapper.deduct(commodityCode, count);
}
}- 账户服务
@Service
public class AccountService {
public void debit(String userId, BigDecimal money) {
accountMapper.debit(userId, money);
}
}测试正常流程:
curl -X POST http://localhost:8081/order/create \
-H "Content-Type: application/json" \
-d '{
"userId": "U1001",
"commodityCode": "C00321",
"count": 2,
"money": 200
}'测试异常回滚:
在账户服务中模拟异常,观察事务回滚。
11. 常见问题与解决方案
问题:服务间调用时XID未传递
解决方案:
- 确保使用Feign或RestTemplate
- 检查Seata依赖版本
- 确保服务注册到同一注册中心
问题:异常抛出但事务未回滚
解决方案:
- 检查
@GlobalTransactional的rollbackFor配置 - 确保异常能够传播
- 检查undo_log表是否创建
问题:使用Seata后性能下降
解决方案:
- 使用DB模式存储
- 优化数据库连接池
- 合理设置超时时间
- 考虑使用TCC模式
问题:数据源未被代理
解决方案:
- 检查Seata依赖
- 确保使用Seata的数据源代理
- 检查配置是否正确
12. 最佳实践
- AT模式:适用于大多数场景,零侵入
- TCC模式:适用于对性能要求高的场景
- Saga模式:适用于长事务场景
使用DB模式:生产环境使用DB模式
连接池优化:合理配置数据库连接池
超时设置:合理设置事务超时时间
批量操作:支持批量操作时使用批量接口
TC集群部署:部署多个TC节点
数据库高可用:使用主从复制
注册中心高可用:使用Nacos集群
事务监控:使用Seata控制台监控事务
日志管理:合理配置日志级别
告警机制:设置事务失败告警
13. 总结与进阶
通过本教程,你已经掌握了:
- ✅ Seata的基本概念和架构
- ✅ AT模式的实现和使用
- ✅ TCC模式的实现和使用
- ✅ Seata与Spring Cloud的集成
- ✅ 配置和部署
- ✅ 常见问题解决
- 源码学习:深入理解Seata的实现原理
- 性能优化:学习性能调优技巧
- 高可用部署:学习集群部署方案
- 其他模式:学习Saga和XA模式
- 监控运维:学习监控和运维实践
- 官方文档:https://seata.io/zh-cn/
- GitHub:https://github.com/seata/seata
- 示例代码:https://github.com/seata/seata-samples
- 从简单开始:先使用AT模式
- 理解原理:深入理解事务原理
- 性能测试:进行性能测试和优化
- 生产实践:在测试环境充分验证
结语
Seata是优秀的分布式事务解决方案,通过本教程的学习,相信你已经掌握了Seata的核心功能和使用方法。
记住:
- 选择合适的模式:根据业务场景选择
- 理解原理:深入理解事务原理
- 性能优化:关注性能问题
- 持续学习:关注Seata新版本特性
祝你学习愉快,编程顺利! 🚀
本教程由Java突击队学习社区编写,如有问题欢迎反馈。