为什么要用分布式事务

业务场景:

系统A是一个电商系统,目前是一台机器部署,系统中有一个用户下订单的接口,但是用户下订单之前一定要去检查一下库存,确保库存足够了才会给用户下单。

由于系统有一定的并发,所以会预先将商品的库存保存在redis中,用户下单的时候会更新redis的库存。

此时系统架构如下:

 

但是这样一来会产生一个问题:假如某个时刻,redis里面的某个商品库存为1,此时两个请求同时到来,其中一个请求执行到上图的第3步,更新数据库的库存为0,但是第4步还没有执行。

而另外一个请求执行到了第2步,发现库存还是1,就继续执行第3步。

这样的结果,是导致卖出了2个商品,然而其实库存只有1个。

很明显不对啊!这就是典型的库存超卖问题

我们很容易想到解决方案:用锁把2、3、4步锁住,让他们执行完之后,另一个线程才能进来执行第2步。

 

按照上面的图,在执行第2步时,使用Java提供的synchronized或者ReentrantLock来锁住,然后在第4步执行完之后才释放锁。

Synchronized(this){

//扣减库存逻辑代码

……

}

这样一来,2、3、4 这3个步骤就被“锁”住了,多个线程之间只能串行化执行。

但是好景不长,整个系统的并发飙升,一台机器扛不住了。现在要增加一台机器,如下图:

 

增加机器之后,系统变成上图所示,我的天!

假设此时两个用户的请求同时到来,但是落在了不同的机器上,那么这两个请求是可以同时执行了,还是会出现库存超卖的问题。

为什么呢?因为上图中的两个A系统,运行在两个不同的JVM里面,他们加的锁只对属于自己JVM里面的线程有效,对于其他JVM的线程是无效的。

因此,这里的问题是:Java提供的原生锁机制在多机部署场景下失效了

这是因为两台机器加的锁不是同一个锁(两个锁在不同的JVM里面)。

那么,我们只要保证两台机器加的锁是同一个锁,问题不就解决了吗?

此时,就该分布式锁隆重登场了,分布式锁的思路是:

在整个系统提供一个全局、唯一的获取锁的“东西”,然后每个系统在需要加锁时,都去问这个“东西”拿到一把锁,这样不同的系统拿到的就可以认为是同一把锁。

至于这个“东西”,可以是Redis、Zookeeper,也可以是数据库。

文字描述不太直观,我们来看下图:

 

通过上面的分析,我们知道了库存超卖场景在分布式部署系统的情况下使用Java原生的锁机制无法保证线程安全,所以我们需要用到分布式锁的方案。

原文地址:为什么要使用分布式锁 - 简书 (jianshu.com)

<think>好的,我现在需要回答用户关于Spring事务管理的局限性、为何不支持分布式事务以及相关解决方案的问题。首先,我需要回顾Spring事务的基本机制。根据引用[1],Spring的事务是声明式事务,基于AOP和动态代理实现的。这意味着它主要适用于单数据源的本地事务管理。 接下来,用户提到Spring不支持分布式事务的原因。我需要结合已有的引用信息来分析。本地事务通常依赖于数据库的ACID特性,而分布式事务涉及多个资源,需要跨服务协调。引用[2]和[3]提到了TCC和2PC等分布式事务解决方案,说明分布式事务需要更复杂的机制,比如资源锁定、协调者等,这可能超出了Spring本地事务的能力范围。 然后,关于局限性,需要指出Spring事务无法跨数据库或服务保持事务一致性,这在分布式环境下是个大问题。比如在微服务架构中,每个服务可能有自己的数据库,这时本地事务无法满足需求。同时,引用[4]提到分布式锁的问题,说明在分布式系统中需要额外的机制来保证一致性,这也是Spring事务不涉及的部分。 解决方案方面,引用[2]和[3]提到的TCC和2PC是常见的分布式事务处理模式。此外,还有基于消息队列的最终一致性方案,如Seata的AT模式。这些方案需要结合具体的技术栈,比如使用Seata框架或者消息中间件来实现跨服务的事务管理。同时,引用[5]提到Spring AOP的动态代理机制,可能在某些情况下需要自定义扩展,但Spring本身并没有内置分布式事务支持,需要依赖外部解决方案。 最后,我需要确保回答结构清晰,分点说明局限性、原因和解决方案,并正确引用相关资料。同时,生成的相关问题要围绕分布式事务处理、具体方案比较以及Spring与分布式事务整合等方向,帮助用户进一步学习。</think>### Spring事务机制的局限性及分布式事务支持问题分析 #### 一、Spring事务的局限性 1. **仅支持单数据源事务** Spring的声明式事务基于数据库的本地事务实现(如JDBC或JPA),其核心是通过$Connection.commit()$和$rollback()$控制单个数据库连接的事务[^1]。在多数据源场景下,无法保证跨数据库的事务一致性。 2. **无法跨服务边界** 在微服务架构中,若涉及多个服务的业务操作(例如订单服务扣库存、支付服务扣款),Spring事务无法通过本地事务机制协调跨服务的事务操作[^2]。 3. **缺乏分布式锁支持** 分布式场景下的资源竞争(如库存超卖)需要分布式锁,而Spring事务未提供此类机制,需依赖Redis等外部工具实现[^4]。 4. **事务传播仅限于本地** Spring的`PROPAGATION_REQUIRES_NEW`等传播行为仅在同一JVM内生效,无法跨进程或跨节点协调事务状态。 --- #### 二、Spring不支持分布式事务的原因 1. **设计定位差异** Spring事务本质是通过AOP代理(CglibAopProxy/JdkDynamicAopProxy)拦截方法调用,在单个线程内管理数据库连接[^5]。而分布式事务需要跨网络协调多个资源管理器(RM),属于系统架构层问题。 2. **协议限制** 本地事务基于X/Open XA协议的单阶段提交,而分布式事务需遵循两阶段提交(2PC)或TCC等协议[^3]。Spring未实现这些协议的协调者逻辑。 3. **性能与复杂度权衡** 分布式事务会引入网络延迟、资源锁定等问题,与Spring追求轻量化的设计目标冲突。例如2PC协议存在阻塞问题,TCC需要业务补偿逻辑[^2]。 --- #### 三、分布式事务解决方案 1. **刚性事务(强一致)** - **XA协议+2PC** 通过DTP模型协调多个资源管理器,但存在协调者单点故障风险。 $$ \text{Phase 1: Prepare} \rightarrow \text{Phase 2: Commit/Rollback} $$ - **Seata AT模式** 通过全局锁+反向SQL日志实现,需配合Seata Server使用。 2. **柔性事务(最终一致)** - **TCC模式** 业务层实现`Try-Confirm-Cancel`接口,适用于高并发场景[^2]。 ```java @Transactional public void createOrder() { inventoryService.tryDeduct(); // Try paymentService.tryFreeze(); // Try // Confirm/Cancel通过MQ异步触发 } ``` - **Saga模式** 通过事件驱动的补偿回滚机制,适合长事务流程。 - **基于消息队列** 使用本地消息表+MQ保证最终一致性,例如订单服务完成本地事务后发送MQ消息触发支付操作。 3. **混合方案** - **Spring Cloud + Seata** 整合Seata的全局事务管理,通过`@GlobalTransactional`注解实现跨服务事务。 - **Redis分布式锁** 结合Redisson实现分布式锁,解决资源竞争问题[^4]。 --- #### 四、方案对比 | 方案 | 一致性 | 性能 | 复杂度 | 适用场景 | |---------------|--------|-------|--------|------------------------| | XA/2PC | 强一致 | 低 | 高 | 传统银行系统 | | TCC | 最终一致| 中 | 高 | 高并发电商交易 | | Saga | 最终一致| 中 | 中 | 长流程业务(如物流) | | 消息队列 | 最终一致| 高 | 低 | 异步通知场景 | ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值