Seata分布式事务实战:从原理到企业级应用
本文将深入解析阿里巴巴Seata分布式事务框架的核心原理,并通过完整的代码示例展示如何在微服务架构中实现高效的事务管理,涵盖AT、TCC、SAGA和XA四种模式的实际应用场景。
一、Seata核心架构与部署
1. Seata服务端部署
# 下载Seata Server (1.5.0版本)
wget https://github.com/seata/seata/releases/download/v1.5.0/seata-server-1.5.0.tar.gz
tar -xzvf seata-server-1.5.0.tar.gz
cd seata/bin
# 修改配置文件 conf/registry.conf
registry {
type = "nacos"
nacos {
serverAddr = "localhost:8848"
namespace = ""
cluster = "default"
}
}
# 启动Seata Server
sh seata-server.sh -p 8091 -h 127.0.0.1 -m file
2. 数据库准备(MySQL示例)
-- 创建全局事务表
CREATE TABLE `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`)
);
-- 创建分支事务表
CREATE TABLE `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`)
);
-- 创建锁表
CREATE TABLE `lock_table` (
`row_key` VARCHAR(128) NOT NULL,
`xid` VARCHAR(96),
`transaction_id` LONG,
`branch_id` LONG,
`resource_id` VARCHAR(256),
`table_name` VARCHAR(32),
`pk` VARCHAR(36),
`gmt_create` DATETIME,
`gmt_modified` DATETIME,
PRIMARY KEY (`row_key`)
);
二、AT模式实战
1. Spring Cloud集成配置
# application.yml 配置
spring:
application:
name: order-service
cloud:
alibaba:
seata:
tx-service-group: my_test_tx_group
datasource:
url: jdbc:mysql://localhost:3306/seata_order?useSSL=false
username: root
password: 123456
driver-class-name: com.mysql.jdbc.Driver
seata:
enabled: true
application-id: ${spring.application.name}
tx-service-group: ${spring.cloud.alibaba.seata.tx-service-group}
service:
vgroup-mapping:
my_test_tx_group: default
grouplist:
default: 127.0.0.1:8091
registry:
type: nacos
nacos:
server-addr: 127.0.0.1:8848
namespace: ""
cluster: default
2. 业务代码示例
// OrderServiceImpl.java
@Service
public class OrderServiceImpl implements OrderService {
@Autowired
private OrderMapper orderMapper;
@Autowired
private AccountFeignClient accountFeignClient;
@Autowired
private StorageFeignClient storageFeignClient;
@Override
@GlobalTransactional(name = "create-order", timeoutMills = 300000, rollbackFor = Exception.class)
public void create(Order order) {
// 1. 创建订单
orderMapper.insert(order);
// 2. 扣减库存(调用库存服务)
storageFeignClient.deduct(order.getCommodityCode(), order.getCount());
// 3. 扣减账户余额(调用账户服务)
accountFeignClient.debit(order.getUserId(), order.getMoney());
// 4. 更新订单状态
order.setStatus(1);
orderMapper.updateById(order);
}
}
// StorageServiceImpl.java
@Service
public class StorageServiceImpl implements StorageService {
@Autowired
private StorageMapper storageMapper;
@Override
@Transactional
public void deduct(String commodityCode, int count) {
storageMapper.deduct(commodityCode, count);
}
}
// AccountServiceImpl.java
@Service
public class AccountServiceImpl implements AccountService {
@Autowired
private AccountMapper accountMapper;
@Override
@Transactional
public void debit(String userId, BigDecimal money) {
accountMapper.debit(userId, money);
}
}
3. MyBatis数据源代理配置
@Configuration
public class DataSourceConfig {
@Bean
@ConfigurationProperties(prefix = "spring.datasource")
public DruidDataSource druidDataSource() {
return new DruidDataSource();
}
@Bean
public DataSource dataSource(DruidDataSource druidDataSource) {
return new DataSourceProxy(druidDataSource);
}
@Bean
public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
factoryBean.setDataSource(dataSource);
return factoryBean.getObject();
}
}
三、TCC模式实现
1. TCC接口定义
// 账户服务TCC接口
@LocalTCC
public interface AccountTccService {
@TwoPhaseBusinessAction(name = "accountTccService", commitMethod = "commit", rollbackMethod = "rollback")
boolean prepare(BusinessActionContext actionContext,
@BusinessActionContextParameter(paramName = "userId") String userId,
@BusinessActionContextParameter(paramName = "money") BigDecimal money);
boolean commit(BusinessActionContext actionContext);
boolean rollback(BusinessActionContext actionContext);
}
// 库存服务TCC接口
@LocalTCC
public interface StorageTccService {
@TwoPhaseBusinessAction(name = "storageTccService", commitMethod = "commit", rollbackMethod = "rollback")
boolean prepare(BusinessActionContext actionContext,
@BusinessActionContextParameter(paramName = "commodityCode") String commodityCode,
@BusinessActionContextParameter(paramName = "count") int count);
boolean commit(BusinessActionContext actionContext);
boolean rollback(BusinessActionContext actionContext);
}
2. TCC实现类
@Service
public class AccountTccServiceImpl implements AccountTccService {
@Autowired
private AccountMapper accountMapper;
@Autowired
private AccountFreezeMapper freezeMapper;
@Override
@Transactional
public boolean prepare(BusinessActionContext actionContext, String userId, BigDecimal money) {
// 检查账户余额
Account account = accountMapper.selectById(userId);
if (account.getBalance().compareTo(money) < 0) {
throw new RuntimeException("账户余额不足");
}
// 冻结金额
AccountFreeze freeze = new AccountFreeze();
freeze.setXid(actionContext.getXid());
freeze.setUserId(userId);
freeze.setFreezeMoney(money);
freeze.setState(AccountFreeze.State.TRY);
freezeMapper.insert(freeze);
// 扣减可用余额
accountMapper.freeze(userId, money);
return true;
}
@Override
@Transactional
public boolean commit(BusinessActionContext actionContext) {
// 获取冻结记录
AccountFreeze freeze = freezeMapper.selectById(actionContext.getXid());
if (freeze == null || freeze.getState() != AccountFreeze.State.TRY) {
return true;
}
// 扣减实际余额
accountMapper.debit(freeze.getUserId(), freeze.getFreezeMoney());
// 更新冻结记录状态
freeze.setState(AccountFreeze.State.CONFIRM);
freezeMapper.updateById(freeze);
return true;
}
@Override
@Transactional
public boolean rollback(BusinessActionContext actionContext) {
// 获取冻结记录
AccountFreeze freeze = freezeMapper.selectById(actionContext.getXid());
if (freeze == null || freeze.getState() != AccountFreeze.State.TRY) {
return true;
}
// 释放冻结金额
accountMapper.unfreeze(freeze.getUserId(), freeze.getFreezeMoney());
// 删除冻结记录
freezeMapper.deleteById(freeze.getXid());
return true;
}
}
四、SAGA模式实现
1. 状态机定义(JSON配置)
{
"Name": "orderSaga",
"Comment": "订单创建SAGA流程",
"StartState": "CreateOrder",
"Version": "1.0",
"States": {
"CreateOrder": {
"Comment": "创建订单",
"ServiceName": "orderService",
"ServiceMethod": "create",
"CompensateServiceName": "orderService",
"CompensateMethod": "cancel",
"Next": "ReduceInventory",
"Input": [
"$.[userId, commodityCode, count, money]"
],
"Output": {
"orderId": "$.id"
},
"Status": {
"#root == true": "SU",
"#root == false": "FA"
}
},
"ReduceInventory": {
"Comment": "扣减库存",
"ServiceName": "storageService",
"ServiceMethod": "deduct",
"CompensateServiceName": "storageService",
"CompensateMethod": "compensateDeduct",
"Next": "DebitBalance",
"Input": [
"$.[commodityCode, count]",
"$.[orderId]"
],
"Output": {},
"Status": {
"#root == true": "SU",
"#root == false": "FA"
}
},
"DebitBalance": {
"Comment": "扣减余额",
"ServiceName": "accountService",
"ServiceMethod": "debit",
"CompensateServiceName": "accountService",
"CompensateMethod": "compensateDebit",
"Next": "Success",
"Input": [
"$.[userId, money]",
"$.[orderId]"
],
"Output": {},
"Status": {
"#root == true": "SU",
"#root == false": "FA"
}
},
"Success": {
"Type": "SUCCESS"
},
"Fail": {
"Type": "FAIL"
}
}
}
2. SAGA执行服务
@Service
public class OrderSagaService {
@Autowired
private StateMachineEngine stateMachineEngine;
public boolean createOrder(Order order) {
Map<String, Object> params = new HashMap<>();
params.put("userId", order.getUserId());
params.put("commodityCode", order.getCommodityCode());
params.put("count", order.getCount());
params.put("money", order.getMoney());
StateMachineInstance instance = stateMachineEngine.startWithBusinessKey(
"orderSaga",
"order_" + System.currentTimeMillis(),
params
);
return instance.getStatus() == StateMachineInstance.SUCCESS;
}
}
五、XA模式实现
1. 数据源配置
# application.yml 配置
spring:
datasource:
url: jdbc:mysql://localhost:3306/seata_order?useSSL=false
username: root
password: 123456
driver-class-name: com.mysql.jdbc.Driver
hikari:
connection-timeout: 30000
idle-timeout: 600000
max-lifetime: 1800000
maximum-pool-size: 20
minimum-idle: 10
seata:
data-source-proxy-mode: XA
2. 业务代码示例
@Service
public class OrderServiceImpl implements OrderService {
@Autowired
private OrderMapper orderMapper;
@Autowired
private AccountMapper accountMapper;
@Autowired
private StorageMapper storageMapper;
@Override
@GlobalTransactional(name = "xa-create-order", timeoutMills = 300000)
public void create(Order order) {
// 1. 创建订单
orderMapper.insert(order);
// 2. 扣减库存
storageMapper.deduct(order.getCommodityCode(), order.getCount());
// 3. 扣减账户余额
accountMapper.debit(order.getUserId(), order.getMoney());
// 4. 更新订单状态
order.setStatus(1);
orderMapper.updateById(order);
}
}
六、Seata高级配置
1. 客户端配置优化
# client.properties
# 事务日志存储模式
client.log.store.mode=db
# 数据库存储配置
client.store.db.datasource=druid
client.store.db.db-type=mysql
client.store.db.driver-class-name=com.mysql.jdbc.Driver
client.store.db.url=jdbc:mysql://127.0.0.1:3306/seata?useSSL=false
client.store.db.user=root
client.store.db.password=123456
client.store.db.min-conn=5
client.store.db.max-conn=30
# 全局事务超时时间(毫秒)
client.tm.degrade-check=false
client.tm.degrade-check-allow-times=10
client.tm.degrade-check-period=2000
client.tm.commit-retry-count=5
client.tm.rollback-retry-count=5
# 分支事务配置
client.rm.report.retry.count=5
client.rm.async.commit.buffer.limit=10000
client.rm.lock.retry.internal=10
client.rm.lock.retry.times=30
2. 服务端高可用配置
# seata-server配置 file.conf
store {
mode = "db"
db {
datasource = "druid"
db-type = "mysql"
driver-class-name = "com.mysql.jdbc.Driver"
url = "jdbc:mysql://127.0.0.1:3306/seata_server?useSSL=false"
user = "root"
password = "123456"
min-conn = 5
max-conn = 100
global.table = "global_table"
branch.table = "branch_table"
lock.table = "lock_table"
query-limit = 100
}
}
service {
vgroup_mapping.my_test_tx_group = "default"
default.grouplist = "127.0.0.1:8091,127.0.0.1:8092,127.0.0.1:8093"
enable-degrade = false
disable-global-transaction = false
}
transport {
shutdown.wait = "3s"
thread-factory.boss-thread-prefix = "NettyBoss"
thread-factory.worker-thread-prefix = "NettyServerNIOWorker"
thread-factory.server-executor-thread-prefix = "NettyServerBizHandler"
thread-factory.share-boss-worker = false
thread-factory.client-selector-thread-prefix = "NettyClientSelector"
thread-factory.client-selector-thread-size = 1
thread-factory.client-worker-thread-prefix = "NettyClientWorkerThread"
thread-factory.boss-thread-size = 1
thread-factory.worker-thread-size = "default"
type = "TCP"
server = "NIO"
heartbeat = true
serialization = "seata"
compressor = "none"
}
七、监控与故障排查
1. Seata控制台集成
# seata控制台配置
server:
port: 7091
spring:
application:
name: seata-console
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306/seata_console?useSSL=false
username: root
password: 123456
seata:
console:
enabled: true
user:
username: admin
password: seata123
2. Metrics监控集成
@Configuration
public class SeataMetricsConfig {
@Bean
public MeterRegistryCustomizer<MeterRegistry> seataMetricsCommonTags() {
return registry -> registry.config().commonTags(
"application", "order-service",
"seata.server", "127.0.0.1:8091"
);
}
@Bean
public SeataMeterRegistry seataMeterRegistry() {
return new SeataMeterRegistry(
new SeataMeterRegistryConfig() {
@Override
public String get(String key) {
return null;
}
@Override
public String prefix() {
return "seata.metrics";
}
},
Clock.SYSTEM
);
}
}
八、最佳实践与性能优化
1. 事务分组规划
# 生产环境推荐的事务分组配置
# 按业务域划分
vgroup_mapping.order-service-group=cluster-a
vgroup_mapping.account-service-group=cluster-b
vgroup_mapping.storage-service-group=cluster-c
# 按环境划分
vgroup_mapping.order-service-dev-group=dev-cluster
vgroup_mapping.order-service-test-group=test-cluster