本地事务实效-分布式架构

本地事务失效 :本以为在同一个事务内的操作,实际上并没有被一起提交或回滚。通常发生在分布式或复杂应用架构中。
核心原因:本地数据库事务(通常指单个数据库上连接的事务)的有效范围,无法覆盖到所有想要一起提交或回滚的操作。

本地事务失效的典型场景

1.跨多个数据源(多个数据库连接)

场景描述 :需要同时操作两个独立的数据库,例如DB1和DB2。
代码示例

java 复制代码
@Transactional
public void transferAcrossDatabase() {
    // 操作一:从DB1的用户表扣钱
    userMapper.deductMoney("userA", 100); // 使用DataSource1连接DB1
    
    // 操作二:往DB2的账户表加钱
    accountMapper.addMoney("userB", 100); // 使用DataSource2连接DB2
}

失效原因

  • 这两个Mapper通常使用不同的DataSource,即两个不同的数据库连接。
  • @Transactional默认只能管理一个数据库连接上的事务。
  • 当deductMoney执行成功后,它的连接会立即提交事务(如果后续代码没出错),而addMoney是在另一个连接上执行,属于另一个独立的事务。此时如果addMoney失败,deductMoney的操作并不会回滚。

2.跨服务方法调用(RPC/HTTP)

在微服务架构中,这是导致数据不一致的"头号杀手"。
场景描述 :在同一个服务内,一个事务方法调用了另一个服务的方法。
代码示例

java 复制代码
@Transactional
public void placeOrder() {
    // 1. 本地数据库操作:创建订单
    orderMapper.insert(order); // 本地事务管理
    
    // 2. 远程服务调用:扣减库存
    inventoryServiceClient.deductStock(productId, quantity); // HTTP或RPC调用
    // 3. 后续本地代码
    ...
}

失效原因

  • 库存服务是另一个独立的进程,有自己独立的数据库。
  • 本地事务只能回滚orderMapper.insert的操作。
  • 如果deductStock调用失败,虽然可以抛出异常回滚导致本地订单创建,但如果deductStock调用成功,而后续本地代码出错,会导致订单回滚,但库存已经被扣减的"脏数据"。

3.非公共方法或方法内调用

这与Spring AOP(面向切面编程)的实现机制有关。
场景描述 :在同一个类中,一个方法调用另一个有@Transactional注解的方法。
代码示例

java 复制代码
public class OrderService {
    
    public void createOrder() {
        // ... 一些逻辑
        this.insertOrder(); // 在类内部调用事务方法
    }
    
    @Transactional
    public void insertOrder() {
        orderMapper.insert(order);
        // 其他数据库操作
    }
}

失效原因

  • Spring的事务管理是通过AOP代理实现的。当从类外部调用insertOrder时,实际上调用的是Spring生成的代理对象的方法,代理对象会开启事务。
  • 当在类内部(如createOrder中)调用this.insertOrder()时,这是目标对象自身的调用,绕过了代理,因此@Transactional注解不会生效。

4.异常被捕获或被"吃掉"

事务的回滚依赖于运行时异常(RuntimeException)和Error。如果异常处理方式不对,事务管理器就不知道需要回滚。
场景描述 :在事务方法中,你把可能抛出异常的地方用try-catch包起来,但没有在catch块中再次抛出异常。
代码示例

java 复制代码
@Transactional
public void updateUser() {
    try {
        userMapper.update(user); // 如果这里出错...
        // ... 其他操作
    } catch (Exception e) {
        logger.error("更新用户失败", e);
        // 只是记录了日志,没有抛出新异常!
        // 事务管理器认为一切正常,会提交事务。
    }
}

失效原因:事务管理器只有在接收到异常信号时才会触发回滚。吞掉异常,就等于告诉框架"一切正常,可以提交"。

5.错误的异常类型

默认情况下,Spring事务只对未检查的异常(即RuntimeException和Error)进行回滚。对已检查异常(如Exception,IOException,SQLException等)不回滚。
场景描述 :方法抛出了一个已检查异常。
代码示例

java 复制代码
@Transactional
public void updateUser() throws Exception { // 声明抛出已检查异常
    userMapper.update(user);
    if (someCondition) {
        throw new Exception("一个业务异常"); // 抛出已检查异常
    }
}

失效原因:即使抛出了异常,但因为它是Exception类型,Spring默认不会回滚事务。

6.手动切断了数据库连接

在一些特殊操作中,可能会手动提交或设置自动提交,干扰了Spring的事务管理。
场景描述 :在事务方法中,手动获取了连接并改变了其自动提交状态。
代码示例

java 复制代码
@Transactional
public void manualConnectionOperation() {
    SomeData data = getFromDatabase();
    // 手动获取连接并操作
    Connection conn = DataSourceUtils.getConnection(dataSource);
    conn.setAutoCommit(true); // 改为自动提交,破坏了事务
    // ... 执行一些SQL,会立即提交
    conn.setAutoCommit(false);
}

失效原因:Spring通过将autoCommit设置为false来管理事务。手动改为了true,会导致SQL语句立即提交,不受事务控制。

如果避免和解决?

  1. 对于场景1(多数据源):使用分布式事务解决方案,如基于XA协议的JTA(如Atomikos)、Seata等。
  2. 对于场景2(跨服务):采用最终一致性方案,如Saga模式、事务消息(RocketMQ)、TCC模式等。
  3. 对于场景3(方法内调用):
    • 将事务方法移到另一个Bean中。
    • 通过AopContext.currentProxy()获取代理对象再调用。
  4. 对于场景4和5(异常处理):
    • 确保在catch块中抛出运行时异常,例如throw new RuntimeException(e);。
    • 使用@Transactional(rollbackFor = Exception.class)注解,强制对所有Exception及其子类都进行回滚。
  5. 对于场景6(手动连接):避免在事务方法中手动操作连接和提交,交由Spring统一管理。

总结

理解本地事务失效的关键在于理解它的边界------它只能管住当前方法通过同一个数据库连接执行的SQL操作。一旦你的操作超出了这个边界(跨连接、跨进程、跨方法调用),本地事务就会失效。

相关推荐
黄林晴4 小时前
Android17引入DeliQueue新架构: 为什么要重写MessageQueue?
架构
学嵌入式的小杨同学4 小时前
STM32 进阶封神之路(三十二):SPI 通信深度实战 —— 硬件 SPI 驱动 W25Q64 闪存(底层时序 + 寄存器配置 + 读写封装)
c++·stm32·单片机·嵌入式硬件·mcu·架构·硬件架构
RestCloud5 小时前
API网关 vs iPaaS:企业集成架构选型的本质差异与2026年选型指南
架构·数据处理·数据传输·ipaas·ai网关·集成平台
Dylan~~~7 小时前
深度解析Cassandra:分布式数据库的王者之路
数据库·分布式
TechWayfarer8 小时前
高并发场景下的IP归属地查询架构:从20ms到0.5ms的优化实践
网络协议·tcp/ip·架构
薛定谔的悦8 小时前
站控显示下级从控EMS的版本信息开发
架构
AI枫林晚8 小时前
源码解析Claude Code 项目 queryLoop 运行机制分析
人工智能·架构
架构师沉默9 小时前
为什么一个视频能让全国人民同时秒开?
java·后端·架构
CoovallyAIHub9 小时前
VisionClaw:智能眼镜 + Gemini + Agent,看一眼就能帮你搜、帮你发、帮你做
算法·架构·github
CoovallyAIHub10 小时前
低空安全刚需!西工大UAV-DETR反无人机小目标检测,参数减少40%,mAP50:95提升6.6个百分点
算法·架构·github