电商项目_核心业务_分布式事务
分布式事务概念
在微服务架构中,完成某一个业务功能可能需要横跨多个服务,操作多个数据库。这就涉及到到了分布式事务。需要操作的资源位于多个资源服务器上,而应用需要保证对于多个资源服务器的数据操作,要么全部成功,要么全部失败。本质上来说,分布式事务就是为了保证不同资源服务器的数据一致性。
分布式事务解决方案
2PC (Two-Phase Commit) (二阶段提交) | TCC (Try-Confirm-Cancel) | 可靠消息 (MQ) | 最大努力通知 | |
一致性 | 强一致性 | 最终一致 | 最终一致 | 最终一致 |
吞吐量 | 低 | 中 | 高 | 高 |
实现复杂度 | 易 | 难 | 中 | 易 |
具体实现方案 | 基于 Seata AT 实现 | 开发者手动实现TCC逻辑 | RocketMq事务消息 | 三方通知消息重试 |
2PC
核心角色:协调者、参与者
阶段一:准备阶段(Prepare Phase)
协调者 向所有参与者发送
Prepare
请求,询问是否可以提交事务。参与者 执行本地事务操作(但不提交),记录 Undo/Redo 日志。
参与者 回复协调者:
同意(Yes):本地事务执行成功,已准备好提交。
中止(No):本地事务失败(如违反约束)。
阶段二:提交/回滚阶段(Commit/Rollback Phase)
若所有参与者均回复 "Yes":
协调者发送Commit
命令,参与者提交事务并释放锁。若有任一参与者回复 "No" 或超时:
协调者发送Rollback
命令,参与者回滚事务(基于日志恢复)。
优点
强一致性:所有节点要么全部提交,要么全部回滚。
简单易懂:流程明确,易于实现。
缺点
同步阻塞:参与者需等待协调者指令,期间资源被锁定。
单点故障:协调者宕机会导致事务阻塞(需超时机制补救)。
数据不一致风险:
若协调者在发送
Commit
后宕机,部分参与者可能未收到指令(需人工干预)。网络分区时可能出现部分提交、部分未提交。
TCC
TCC 是一种 柔性事务解决方案,通过业务逻辑拆分为三个阶段(Try、Confirm、Cancel)实现分布式事务的最终一致性。
Try(尝试):
预留资源:检查并锁定资源(如冻结库存、预扣金额)。
若所有参与方 Try 成功,进入 Confirm 阶段;否则进入 Cancel 阶段。
Confirm(确认):
提交业务:基于 Try 阶段的预留资源,完成最终操作(如扣减库存、实际支付)。
Confirm 必须保证幂等性(重复调用不产生副作用)。
Cancel(取消):
回滚补偿:释放 Try 阶段预留的资源(如解冻库存、返还预扣金额)。
Cancel 也必须保证幂等性。
🔹 关键特点:TCC 是 业务侵入式 的,需要开发者手动实现 Try/Confirm/Cancel 的逻辑。
2PC案例
1. Apache ShardingSphere 分布式事务
整合 Seata AT 事务时,需要将 TM,RM 和 TC 的模型融入 Apache ShardingSphere 的分布式事务生态中。 在数据库资源上,Seata 通过对接 DataSource 接口,让 JDBC 操作可以同 TC 进行远程通信。 同样,Apache ShardingSphere 也是面向 DataSource 接口,对用户配置的数据源进行聚合。 因此,将 DataSource 封装为 基于 Seata 的 DataSource 后,就可以将 Seata AT 事务融入到 Apache ShardingSphere 的分片生态中。
2. Seata架构
Seata架构中的三个角色:
- TC(Transaction Coordinator) 事务协调者: 单独部署的Server服务
- TM(Transaction Manager)事务管理器:发起事务的微服务 嵌入TM
- RM(Resource Manager)资源管理器:事务参数的微服务嵌入RM
3. 整合Seata实战
1. 微服务引入seta版本依赖
<dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-seata</artifactId><version>2.2.8.RELEASE</version><exclusions><exclusion> -- 排除spring-cloud下的seata, 单独引入<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>
2. 微服务对应的数据库中 添加undo log表(仅AT模式)
(2PC模式下第一阶段记录的日志)
3. 微服务application.yml添加seata配置
注册中心、配置中心
4. 全局事务发起者开启全局事务配置
订单表使用了分库分表技术(ShardingSphere),seata 不能对逻辑表进行解析。解决方案:
整合 Seata AT 事务时,需要将 TM,RM 和 TC 的模型融入 Apache ShardingSphere 的分布式事务生态中。
ShardingSphere整合seata
1)引入依赖
<!--shardingsphere 整合 seata 依赖-->
<dependency>
<groupId>org.apache.shardingsphere</groupId>
<artifactId>sharding-transaction-base-seata-at</artifactId>
<version>4.1.1</version>
</dependency>
2)配置 seata.conf
3)开启全局事务配置
//全局事务交给 SeataATShardingTransactionManager 管理
@ShardingTransactionType(TransactionType.BASE)
@Transactional
public CommonResult generateOrder(OrderParam orderParam, Long memberId) {
可靠消息案例
就是rocketMq的事务消息保证事务性,之前已经反复分析过多次。
电商系统中典型分布式事务案例
1、用户下单冻结库存
OmsPortalOrderServiceImpl#generateOrder
begin 分布式事务
1. 库存锁定
-- 所谓库存的锁定,库存表是真正的扣减了, 只不过会记录分布式事务日志表,如果需要回滚,根据事务日志表回滚库存信息
<update id="lockStockByExample" parameterType="java.lang.Integer">update pms_sku_stocksetstock = stock - #{lockQuantity,jdbcType=INTEGER},lock_stock = lock_stock + #{lockQuantity,jdbcType=INTEGER}<if test="_parameter != null"><include refid="Update_By_Example_Where_Clause" /></if></update>
一个订单下可能有多个商品,会遍历商品,逐个锁库存。
2. order表插入记录
3. order_item表插入记录
end 分布式事务
2、支付成功后修改订单状态,异步扣减真实库存
OmsPortalOrderServiceImpl#paySuccess
1. 修改订单表的支付状态(实际情况应该配置个支付记录表)
2. 进行真实库存的扣减,可以使用MQ消息进行异步库存扣减
-- 冻结的值加回去
<update id="updateSkuStock">UPDATE pms_sku_stockSETlock_stock = CASE id<foreach collection="itemList" item="item">WHEN #{item.productSkuId} THEN lock_stock - #{item.changesCount}</foreach>ENDWHEREid IN<foreach collection="itemList" item="item" separator="," open="(" close=")">#{item.productSkuId}</foreach></update>