微服务架构下分布式事务的必要性判断
微服务拆分后,一个业务操作往往跨越多个服务,每个服务有独立的数据库,本地事务无法保证跨服务的数据一致性。但并非所有跨服务操作都需要分布式事务——很多场景可以通过最终一致性、补偿机制和幂等设计来规避,引入分布式事务框架的成本(性能损耗、运维复杂度、故障面扩大)不低。
需要引入分布式事务的典型场景:
– 资金类操作:余额扣减+流水记录必须原子化
– 库存+订单:超卖不可接受,必须强一致
– 多步骤业务流程:中间步骤失败需要回滚已完成的步骤
可以不用分布式事务的场景:
– 跨服务的数据只读查询
– 非关键数据的异步同步(如搜索索引更新)
– 可以通过消息队列实现最终一致性的操作
Seata AT模式:零侵入的自动分布式事务
Seata的AT(Automatic Transaction)模式通过代理数据源实现自动的两阶段提交,对业务代码侵入最小。核心原理:一阶段拦截SQL解析语义,生成回滚日志(undo_log),二阶段根据回滚日志自动提交或回滚。
Seata Server(TC事务协调器)部署:
# docker-compose-seata.yaml
version: '3.8'
services:
seata-server:
image: seataio/seata-server:2.2.0
ports:
- '8091:8091'
environment:
- SEATA_IP=seata-server
volumes:
- ./seata-config:/seata-server/resources
depends_on:
- seata-db
seata-db:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: seata_pwd
MYSQL_DATABASE: seata
volumes:
- ./seata-init.sql:/docker-entrypoint-initdb.d/init.sql
Spring Boot 3集成Seata AT模式:
// pom.xml核心依赖
<dependency>
<groupId>io.seata</groupId>
<artifactId>seata-spring-boot-starter</artifactId>
<version>2.2.0</version>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-seata</artifactId>
<version>2023.0.1.2</version>
</dependency>
// application.yml
seata:
enabled: true
application-id: order-service
tx-service-group: my-test-group
service:
vgroup-mapping:
my-test-group: default
registry:
type: nacos
nacos:
server-addr: nacos:8848
namespace: seata
每个参与事务的微服务数据库需要建undo_log表:
-- 每个业务库执行
CREATE TABLE IF NOT EXISTS `undo_log` (
`branch_id` BIGINCT NOT NULL,
`xid` VARCHAR(128) NOT NULL,
`context` VARCHAR(128) NOT NULL,
`rollback_info` LONGBLOB NOT NULL,
`log_status` INT(11) NOT NULL,
`log_created` DATETIME(6) NOT NULL,
`log_modified` DATETIME(6) NOT NULL,
UNIQUE KEY `ux_undo_log` (`xid`, `branch_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
业务代码只需在需要分布式事务的方法上加@GlobalTransactional注解:
@Service
public class OrderService {
@GlobalTransactional(timeoutMills = 30000, name = 'create-order')
public OrderResult createOrder(CreateOrderRequest request) {
// 1. 创建订单(本地事务)
Order order = orderMapper.insert(buildOrder(request));
// 2. 调用库存服务扣减库存(远程事务)
inventoryClient.deduct(request.getProductId(), request.getQuantity());
// 3. 调用账户服务扣减余额(远程事务)
accountClient.debit(request.getUserId(), order.getTotalAmount());
return OrderResult.success(order);
}
}
AT模式的全局锁机制是性能关键——一阶段本地提交后,Seata会锁定相关行,直到全局事务提交或回滚。高并发场景下全局锁争用严重,AT模式的TPS会显著下降。
Saga模式:长流程的补偿式事务
当业务流程涉及多个步骤且部分步骤无法回滚(如已发出的邮件、已调用的第三方API),AT模式的全局回滚不再适用。Saga模式通过正向执行+补偿回滚实现最终一致性。
Seata Saga的状态机定义(JSON DSL):
// saga-order.json — 订单创建Saga流程
{
'Name': 'create-order-saga',
'Comment': '订单创建Saga流程',
'StartState': 'CreateOrder',
'States': {
'CreateOrder': {
'Type': 'ServiceTask',
'ServiceName': 'orderService',
'ServiceMethod': 'create',
'CompensateMethod': 'cancel',
'Next': 'DeductInventory',
'Input': ['$input.order']
},
'DeductInventory': {
'Type': 'ServiceTask',
'ServiceName': 'inventoryService',
'ServiceMethod': 'deduct',
'CompensateMethod': 'restore',
'Next': 'DebitAccount',
'Input': ['$input.order.productId', '$input.order.quantity']
},
'DebitAccount': {
'Type': 'ServiceTask',
'ServiceName': 'accountService',
'ServiceMethod': 'debit',
'CompensateMethod': 'credit',
'Next': 'Succeed',
'Input': ['$input.order.userId', '$input.order.totalAmount']
},
'Succeed': {
'Type': 'Succeed'
},
'Fail': {
'Type': 'Fail'
}
}
}
补偿方法必须满足幂等性——网络超时重试可能导致补偿方法被多次调用:
@Service
public class InventoryService {
// 正向操作
public boolean deduct(String productId, int quantity) {
int affected = inventoryMapper.deduct(productId, quantity);
if (affected == 0) {
throw new BusinessException('库存不足');
}
return true;
}
// 补偿操作(幂等)
public boolean restore(String productId, int quantity) {
// 乐观锁版本号保证幂等
inventoryMapper.restore(productId, quantity);
return true;
}
}
AT vs Saga的选型决策矩阵
| 维度 | AT模式 | Saga模式 |
|——|——–|———-|
| 一致性 | 强一致(短暂隔离期) | 最终一致 |
| 侵入性 | 低(仅加注解+undo_log) | 中(需写补偿方法) |
| 性能 | 全局锁限制并发 | 无全局锁,性能好 |
| 适用流程长度 | 3~5步 | 5步以上长流程 |
| 回滚能力 | 自动回滚 | 业务补偿 |
| 第三方服务 | 不支持(无法代理外部SQL) | 支持补偿 |
混合场景的推荐方案:核心资金链路用AT模式保证强一致,外围通知类操作用Saga或消息队列异步处理。
生产环境的运维与容灾
Seata Server的可用性直接影响所有分布式事务,必须做高可用部署:
# Seata Server多副本+Nacos注册
seata:
server:
raft:
cluster: 'seata-server1:8091,seata-server2:8091,seata-server3:8091'
snapshot-interval: 600
# 关键运维操作
# 1. 查看挂起的全局事务
curl 'http://seata-server:8091/api/v1/global-session?status=Begin'
# 2. 强制回滚超时事务
curl -X DELETE 'http://seata-server:8091/api/v1/global-session/{xid}'
# 3. undo_log清理(全局事务提交后可安全删除)
DELETE FROM undo_log WHERE log_status = 9 AND log_modified < DATE_SUB(NOW(), INTERVAL 7 DAY)
分布式事务不是微服务的标配——是最后的选择。在引入之前,先评估业务能否接受最终一致性、能否通过设计规避(如合并服务边界、使用事件驱动架构)。只有强一致性的刚性需求才值得付出分布式事务的复杂度代价。
原创文章,作者:小编,如若转载,请注明出处:https://www.yunthe.com/springboot3-wei-fu-wu-fen-bu-shi-shi-wu-shi-zhan-seataat-mo/