一、分布式事务概述
1.1 什么是分布式事务?
分布式事务是指在分布式系统中,由一组操作组成的、跨多个服务或数据库的事务。这些操作要么全部成功提交,要么全部回滚,以保证数据的最终一致性。
在微服务架构下,传统的单数据库事务无法满足跨服务的数据一致性要求,因此必须引入分布式事务机制。
1.2 电商项目中的分布式事务场景
电商下单链路是典型的分布式事务场景,涉及两个关键操作:
用户下单冻结库存:
OmsPortalOrderServiceImpl#generateOrder支付成功后修改订单状态,异步扣减真实库存:
OmsPortalOrderServiceImpl#paySuccess
这两个操作分别属于订单服务和库存服务,需要保证它们的一致性。
二、常见分布式事务解决方案对比
下表对比了四种主流分布式事务解决方案:
| 方案 | 一致性 | 吞吐量 | 实现复杂度 |
|---|---|---|---|
| 2PC | 强一致性 | 低 | 易 |
| TCC | 最终一致 | 中 | 难 |
| 可靠消息 | 最终一致 | 高 | 中 |
| 最大努力通知 | 最终一致 | 高 | 易 |
电商项目中重点讲解两种方案:
2PC方案:基于Seata AT模式实现
可靠消息方案:基于RocketMQ事务消息实现
三、基于Seata实现用户下单冻结库存场景的分布式事务
3.1 Seata架构简介
Seata架构包含三个核心角色:
TC(Transaction Coordinator):事务协调者,维护全局事务和分支事务状态,驱动全局事务提交或回滚。
TM(Transaction Manager):事务管理器,定义全局事务范围(开始、提交、回滚)。
RM(Resource Manager):资源管理器,管理分支事务处理的资源,与TC交互注册分支事务并报告状态。
3.2 整合Seata实战
3.2.1 版本选择
注意组件兼容性。电商项目使用Spring Cloud Alibaba 2.2.6,整合Seata 1.3.0。但推荐使用更高版本(如1.5.x),此处选择:
Seata Server:1.5.2
客户端依赖:1.5.2
3.2.2 依赖引入
xml
<dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-seata</artifactId> <version>2.2.8.RELEASE</version> <exclusions> <exclusion> <groupId>io.seata</groupId> <artifactId>seata-spring-boot-starter</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>io.seata</groupId> <artifactId>seata-spring-boot-starter</artifactId> <version>1.5.2</version> </dependency>
3.2.3 数据库表准备(AT模式)
在每个微服务对应的业务数据库中创建undo_log表:
sql
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(11) 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 AUTO_INCREMENT = 1 DEFAULT CHARSET = utf8mb4 COMMENT ='AT transaction mode undo table';
3.2.4 微服务配置
在application.yml中添加Seata配置:
yaml
seata: application-id: wolfmall-product tx-service-group: wolf-order-group 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 namespace: 7e838c12-8554-4231-82d5-6d93573ddf32 group: SEATA_GROUP />3.3.2 具体步骤
引入依赖:
xml
<dependency> <groupId>org.apache.shardingsphere</groupId> <artifactId>sharding-transaction-base-seata-at</artifactId> <version>4.1.1</version> </dependency>
配置seata.conf:根据seata.conf配置适配数据源为Seata所需的DataSourceProxy。
开启全局事务:
java
// 全局事务交给 SeataATShardingTransactionManager 管理 @ShardingTransactionType(TransactionType.BASE) @Transactional public CommonResult generateOrder(OrderParam orderParam, Long memberId) { // 业务逻辑 }重要:
@GlobalTransactional和@ShardingTransactionType不能同时使用。需关闭数据源自动代理:
seata.enable-auto-data-source-proxy: false四、柔性事务:可靠消息最终一致性方案实现
可靠消息最终一致性方案强调:事务发起方完成本地事务后发出一条消息,事务参与方(消费者)必须能接收并处理该消息,最终达到数据一致。
4.1 本地消息表方案
该方案最初由eBay提出,核心是通过本地事务保证业务操作和消息的一致性,再通过定时任务将消息发送至消息中间件,待确认消费成功后删除消息。
以注册送优惠券为例:
涉及两个微服务:会员服务(添加用户)和优惠券服务(赠送优惠券)。
流程:
用户注册:会员服务在本地事务中新增用户并记录“优惠券消息日志”。
sql
BEGIN TRANSACTION; -- 1. 新增用户 -- 2. 存储优惠券消息日志 COMMIT;
定时任务扫描日志:独立线程定时扫描消息日志表,将消息发送至消息中间件,发送成功后删除日志,失败则等待下一周期重试。
消费消息:优惠券服务监听MQ,接收到消息后赠送优惠券,处理成功后返回ACK。若处理失败,MQ会重复投递,因此消费端需实现幂等性。
4.2 RocketMQ事务消息方案
RocketMQ事务消息主要解决Producer端消息发送与本地事务执行的原子性问题。它将本地消息表封装到MQ内部,简化了实现。
执行流程(以注册送优惠券为例):
Producer发送事务消息:发送消息至MQ Server,消息状态为Prepared(消费者不可见)。
MQ Server回应发送成功。
Producer执行本地事务:执行添加用户操作。
消息投递:
若本地事务成功,Producer发送commit,MQ Server将消息标记为可消费。
若本地事务失败,Producer发送rollback,MQ Server删除消息。
事务回查:若Producer执行本地事务时挂掉或超时,MQ Server会进行事务回查,根据回查结果决定是否投递消息。
代码实现:
开发者需实现
RocketMQLocalTransactionListener接口:java
public interface RocketMQLocalTransactionListener { RocketMQLocalTransactionState executeLocalTransaction(Message msg, Object arg); RocketMQLocalTransactionState checkLocalTransaction(Message msg); }
executeLocalTransaction:发送prepare消息成功后回调,用于执行本地事务。
checkLocalTransaction:用于事务回查,判断本地事务状态。五、总结
分布式事务是微服务架构中的关键挑战,电商项目中的下单链路是典型场景。本文通过对比分析,介绍了两种主流解决方案:
基于Seata的2PC方案:适用于对一致性要求高的场景,如冻结库存。通过TC、TM、RM协作,保证强一致性。整合时需注意与ShardingSphere等分库分表组件的兼容性。
基于RocketMQ的可靠消息方案:适用于最终一致性场景,如注册送优惠券。通过事务消息机制,简化了本地消息表的实现,保证了Producer端消息发送与本地事务的原子性。
在实际项目中,应根据业务场景的一致性要求、吞吐量需求和系统复杂度,选择合适的分布式事务方案。同时,需关注方案的容错性、幂等性设计和监控告警,确保系统稳定可靠。