不要在事务中操作中间件了

本文探讨了在处理数据库操作和中间件交互时如何确保事务的原子性,以避免数据不一致。通过分析异常场景,提出了两种解决方案:一是重构代码,将中间件操作移出事务;二是利用Spring的TransactionSynchronizationManager在事务提交后执行中间件操作。这两种方法旨在优化代码,保证程序执行的正确性并减少大事务带来的问题。
摘要由CSDN通过智能技术生成

在大部分场景中,都会有同时操作数据库,并且需要操作中间件的场景,例如:操作数据库同时需要向redis、mq中保存数据或者发送消息,这时候就会涉及到,如果中间件报错影响数据入库怎么办?如果在操作中间件之后报错,数据库回滚,中间件的数据如何补偿?

这些问题,在正常的场景中不会有问题,但是在某些场景,会导致数据错误或者某个环节的功能无法工作,所以如何在事务中保证数据库和中间件操作的原子性呢

原因分析

  • 例如以下伪代码

    主要功能为:保存订单数据到数据库,并且向mq中发送消息,通知其他服务发送订单成功的短信,并保存订单的数据到redis中做数据统计和校验,并调用第三方服务进行数据传递

    @Transactional
    public void orderSave(Order order, User user) {
        // 保存订单
        orderDao.save(order);   // 步骤1
        // 更新订单用户表
        userOrderDao.update(order, user); // 步骤2
        // 通知短信服务发送短信
        rabbitMqTemplate.sendMsg(order); // 步骤3
        // 保存订单到redis中
        redisTemplate.setCache("dev:user:order:success:" + user.getId(), order); // 步骤4
        // 调用第三方接口,数据传递
        thrityPartService.notify(order); // 步骤5
    }
    
    

    这种场景应该是一个较为常见的场景,有多种操作需要去进行做,并且在操作数据库时需要通过事务保证原子性和数据的一致性

  • 异常场景

    • 第一种:(正常)

      步骤1操作成功,步骤2报错,那么数据库回滚,后续的3、4、5都不会执行,保证了数据和程序的正确执行

    • 第二种:(异常)

      步骤1步骤2步骤3步骤4均成功,但是在步骤5报错,因为方法上有事务,所以会发生回滚,数据库的数据会被还原,但是步骤3、4都是操作中间件,无法和数据库在同一个原子性操作中,数据库的@Transactional事务注解无法控制操作中间件等第三方程序,所以数据库中数据回滚,但是redismq等无法回滚,会通知第三方服务,可能会导致数据不一致。

  • 解决方案

    • 第一种:

      重构现有代码,将操作中间件的操作和调用第三方的代码(与数据库无关的代码)均移出事务中,在事务操作完成后的代码中执行其他的代码。

      可能需要将多个无关的返回值进行封装,然后再事务方法外操作数据

    • 第二种

      使用Spring提供的事务的功能,依然可以保持在事务中调用方法,但是如果方法上有事务,那么则在事务提交之后才会调用该方法

      @Transactional
      public void orderSave(Order order, User user) {
          // 保存订单
          orderDao.save(order);   // 步骤1
          // 更新订单用户表
          userOrderDao.update(order, user); // 步骤2
      	
          // 调用方法
          orderOperateAfterTransaction(order, user);
      }
      
      
      /**
       * 如果没有事务则直接调用目标方法,如果有事务,则在事务提交之后调用目标方法
       * 
       */
      public void orderOperateAfterTransaction(Order order, User user) {
          if (!TransactionSynchronizationManager.isSynchronizationActive()) {
              orderOperate(Order order, User user);
          } else {
              TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {
                  @Override
                  public void afterCommit() {
                      orderOperate(Order order, User user);
                  }
              });
          }
      }
      
      /**
       * 操作中间件的方法,保证数据库操作成功,事务提交之后再执行
       */
      public void orderOperate(Order order, User user) {
          // 通知短信服务发送短信
          rabbitMqTemplate.sendMsg(order);
          // 保存订单到redis中
          redisTemplate.setCache("dev:user:order:success:" + order.getId(), order);
          // 调用第三方接口,数据传递
          thrityPartService.notify(order);
      }
      

    不要再继续在事务方法中操作中间件了,会导致数据的不一致以及程序的执行问题,可以通过上述的方法来优化代码,保证程序的执行,并且减少大事务的产生,防止数据库连接占满,不仅提交效率,并且更好的优化代码~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值