您好,登录后才能下订单哦!
# Seata如何搭建与分布式事务入门
## 目录
1. [分布式事务基础概念](#一分布式事务基础概念)
- 1.1 [什么是分布式事务](#11-什么是分布式事务)
- 1.2 [CAP理论与BASE理论](#12-cap理论与base理论)
- 1.3 [常见解决方案对比](#13-常见解决方案对比)
2. [Seata核心架构解析](#二seata核心架构解析)
- 2.1 [TC/TA/RM角色划分](#21-tctarm角色划分)
- 2.2 [AT模式原理详解](#22-at模式原理详解)
- 2.3 [TCC/SAGA模式对比](#23-tccsaga模式对比)
3. [Seata Server部署实战](#三seata-server部署实战)
- 3.1 [单机版部署指南](#31-单机版部署指南)
- 3.2 [高可用集群配置](#32-高可用集群配置)
- 3.3 [注册中心与配置中心](#33-注册中心与配置中心)
4. [Spring Cloud集成实践](#四spring-cloud集成实践)
- 4.1 [Spring Boot Starter配置](#41-spring-boot-starter配置)
- 4.2 [数据源代理配置](#42-数据源代理配置)
- 4.3 [全局事务注解使用](#43-全局事务注解使用)
5. [生产环境优化建议](#五生产环境优化建议)
- 5.1 [参数调优指南](#51-参数调优指南)
- 5.2 [常见问题排查](#52-常见问题排查)
- 5.3 [监控与告警配置](#53-监控与告警配置)
6. [实战案例演示](#六实战案例演示)
- 6.1 [电商下单场景](#61-电商下单场景)
- 6.2 [跨行转账场景](#62-跨行转账场景)
## 一、分布式事务基础概念
### 1.1 什么是分布式事务
在单体应用架构中,我们通常使用本地事务(如MySQL的InnoDB引擎事务)来保证数据一致性。但当系统演进为微服务架构时,一个业务操作往往需要跨多个服务、多个数据库,这就产生了分布式事务需求。
**典型场景示例**:
```java
// 伪代码展示分布式事务场景
@DistributedTransactional
public void createOrder(OrderDTO order) {
// 操作1:库存服务扣减库存
inventoryService.reduceStock(order.getItems());
// 操作2:订单服务创建订单
orderService.create(order);
// 操作3:账户服务扣减余额
accountService.debit(order.getUserId(), order.getAmount());
}
分布式系统必须满足P,因此在C和A之间需要权衡。Seata采用的AT模式优先保证AP,最终达到C。
方案 | 原理 | 优点 | 缺点 |
---|---|---|---|
2PC | 两阶段提交 | 强一致性 | 同步阻塞、单点问题 |
TCC | Try-Confirm-Cancel | 高灵活性 | 开发复杂度高 |
SAGA | 长事务拆分 | 适合长流程 | 无隔离性 |
本地消息表 | 异步确保型 | 简单可靠 | 时效性差 |
Seata AT | 反向SQL补偿 | 零侵入、高性能 | 全局锁竞争 |
阶段一:业务数据+回滚日志入库
-- 原始SQL
UPDATE product SET stock = stock - 1 WHERE id = 1001;
-- Seata代理后实际执行
-- 1. 查询前镜像
SELECT stock FROM product WHERE id = 1001;
-- 2. 执行业务SQL
UPDATE product SET stock = stock - 1 WHERE id = 1001;
-- 3. 查询后镜像
SELECT stock FROM product WHERE id = 1001;
-- 4. 插入undo_log
INSERT INTO undo_log VALUES(...);
阶段二提交流程: 1. TM向TC发起全局提交 2. TC异步删除各分支undo_log
阶段二回滚流程: 1. TM向TC发起全局回滚 2. TC查询undo_log构造补偿SQL
UPDATE product SET stock = stock + 1 WHERE id = 1001;
public interface AccountService {
@TwoPhaseBusinessAction(name = "debit", commitMethod = "confirm", rollbackMethod = "cancel")
boolean tryDebit(@BusinessActionContextParameter(paramName = "userId") String userId,
@BusinessActionContextParameter(paramName = "money") BigDecimal money);
boolean confirm(BusinessActionContext context);
boolean cancel(BusinessActionContext context);
}
步骤1:下载安装包
wget https://github.com/seata/seata/releases/download/v1.5.2/seata-server-1.5.2.tar.gz
tar -zxvf seata-server-1.5.2.tar.gz
步骤2:修改conf/registry.conf
registry {
type = "nacos"
nacos {
serverAddr = "127.0.0.1:8848"
namespace = ""
cluster = "default"
}
}
步骤3:启动服务
sh bin/seata-server.sh -p 8091 -h 127.0.0.1
数据库存储模式配置(conf/file.conf):
store {
mode = "db"
db {
datasource = "druid"
dbType = "mysql"
url = "jdbc:mysql://127.0.0.1:3306/seata"
user = "root"
password = "123456"
minConn = 5
maxConn = 30
}
}
需要初始化的数据库表:
-- 全局事务表
CREATE TABLE IF NOT EXISTS global_table (
xid VARCHAR(128) NOT NULL PRIMARY KEY,
...
);
-- 分支事务表
CREATE TABLE IF NOT EXISTS branch_table (
branch_id BIGINT NOT NULL PRIMARY KEY,
xid VARCHAR(128) NOT NULL,
...
);
Nacos配置中心同步: 1. 将config.txt上传到Nacos
python3 nacos-config.py -h 127.0.0.1 -p 8848 -g SEATA_GROUP -t 5a3c7d6c-f497-4d68-a71a-2e5e1230
pom.xml依赖:
<dependency>
<groupId>io.seata</groupId>
<artifactId>seata-spring-boot-starter</artifactId>
<version>1.5.2</version>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-seata</artifactId>
</dependency>
application.yml配置:
seata:
application-id: order-service
tx-service-group: my_tx_group
service:
vgroup-mapping:
my_tx_group: default
registry:
type: nacos
nacos:
server-addr: 127.0.0.1:8848
必须配置项:
@Configuration
public class DataSourceConfig {
@Bean
@ConfigurationProperties(prefix = "spring.datasource")
public DruidDataSource druidDataSource() {
return new DruidDataSource();
}
@Primary
@Bean("dataSource")
public DataSource dataSource(DruidDataSource druidDataSource) {
return new DataSourceProxy(druidDataSource);
}
}
基础用法:
@GlobalTransactional(timeoutMills = 60000, name = "createOrder")
public void createOrder(Order order) {
// 1. 扣减库存
storageFeignService.deduct(order.getCommodityCode(), order.getCount());
// 2. 创建订单
orderMapper.insert(order);
// 3. 扣减余额
accountFeignService.debit(order.getUserId(), order.getMoney());
}
注意事项: 1. 避免在事务方法内处理HTTP请求 2. 超时时间要大于所有分支事务耗时总和 3. 建议在入口Controller方法上声明
关键server端参数(seata-server/conf/file.conf):
transport {
thread-factory {
boss-thread-prefix = "NettyBoss"
worker-thread-prefix = "NettyServerNIOWorker"
server-executor-thread-prefix = "NettyServerBizHandler"
share-boss-worker = false
client-selector-thread-prefix = "NettyClientSelector"
client-selector-thread-size = 1
client-worker-thread-prefix = "NettyClientWorkerThread"
worker-thread-size = "default"
}
}
客户端优化建议:
seata:
client:
rm-report-success-enable: false # 关闭分支执行结果上报
report-retry-count: 5 # 重试次数
全局锁冲突处理: 1. 检查业务SQL条件是否走索引 2. 减少单个事务涉及的数据范围 3. 适当调整隔离级别:
@GlobalTransactional(lockRetryInternal = 100, lockRetryTimes = 30)
事务悬挂问题: - 现象:分支事务先于全局事务注册 - 解决方案:升级到1.5.0+版本,开启:
seata:
client:
undo:
data-validation: true
Prometheus监控配置:
metrics {
enabled = true
registry-type = "compact"
exporter-list = "prometheus"
exporter-prometheus-port = 9898
}
Grafana监控看板: 1. 导入官方Dashboard(ID:10477) 2. 关键监控指标: - seata.transaction.active.count - seata.transaction.committed.rate - seata.transaction.rollback.rate
业务流程图:
sequenceDiagram
participant C as Client
participant O as OrderService
participant S as StorageService
participant A as AccountService
C->>O: 提交订单
O->>S: 扣减库存
S-->>O: 操作成功
O->>A: 扣减余额
A-->>O: 操作成功
O->>O: 生成订单
O-->>C: 下单成功
异常处理设计: 1. 库存不足:立即返回错误 2. 余额不足:触发全局回滚 3. 网络超时:依赖重试机制
TCC模式实现:
// 转账服务接口
public interface TransferService {
@TwoPhaseBusinessAction(name = "transfer", commitMethod = "confirm", rollbackMethod = "cancel")
boolean tryTransfer(@BusinessActionContextParameter(paramName = "from") String from,
@BusinessActionContextParameter(paramName = "to") String to,
@BusinessActionContextParameter(paramName = "amount") BigDecimal amount);
}
// 账户服务实现
@Service
public class AccountServiceImpl implements AccountService {
@Transactional
public boolean tryDebit(String accountNo, BigDecimal amount) {
// 检查账户状态
// 冻结部分金额
accountMapper.freezeAmount(accountNo, amount);
return true;
}
public boolean confirm(BusinessActionContext context) {
// 实际扣减冻结金额
String accountNo = (String)context.getActionContext("from");
BigDecimal amount = (BigDecimal)context.getActionContext("amount");
accountMapper.debit(accountNo, amount);
accountMapper.unfreezeAmount(accountNo, amount);
return true;
}
}
SAGA模式设计:
// 状态机定义示例
{
"name": "bankTransferSaga",
"steps": [
{
"name": "deductFromAccount",
"compensate": "compensateDeduct",
"service": "accountService",
"input": ["$.['from']", "$.['amount']"]
},
{
"name": "addToAccount",
"compensate": "compensateAdd",
"service": "accountService",
"input": ["$.['to']", "$.['amount']"]
}
]
}
作者建议:在实际项目中使用Seata时,建议从AT模式开始,随着业务复杂度提升逐步考虑TCC或SAGA模式。同时要特别注意undo_log表的大小控制,建议定期归档历史数据。 “`
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。