面对接口中的大事务,你需要考虑这些

什么是大事务

在日常的开发过程中,后端人员需要根据业务需求给前端的小伙伴提供一个可调用的服务接口,在这个接口中可能会涉及数据查询、远程服务调用、组装数据计算以及最终落库等操作,为了保证这些操作数据的可靠性就需要使用到事务。随着数据量的增长以及其它远程服务的调用时间长等情况,接口中需要执行的SQL比较多,导致最终事务的执行时间拉长,这就是所谓的大事务。

大事务的隐患

  • 1、占用数据库连接
  • 2、锁定数据增多,造成大量的锁阻塞和响应超时
  • 3、执行时间过长,增加主从的延迟
  • 4、比较难回滚
  • 5、占用日志空间

优化方案

那么针对上面的这些隐患,作为优秀的后端开发人员该如何进行优化处理呢?

使用编程式事务

为了提高开发效率,我们常常习惯于在业务方法上添加声明式事务@Transactional注解来进行事务处理。不过声明式事务不够灵活,加上了就意味着整个业务方法对于数据库的操作都要加入到事务中,包括查询操作。我们只需要将update、insert加入事务管理,通过使用编程式事务灵活的控制事务的范围,方便后续的数据回滚。

csharp 复制代码
  transactionTemplate.executeWithoutResult(transactionStatus -> {
   try {
      // 生成订单
      // 生成订单明细
      // 扣库存
      // 核销优惠券
      // 扣积分
      // 生成订单日志
      } catch(Exception e) {
        log.error("订单创建异常, error: ", e);
        transactionStatus.setRollbackOnly();
      }
    }
  )

查询放到事务外侧

将查询的方法放到服务的外侧,也是能够有效解决大事务的一种方案,因为查询方法不需要使用到事务,那么只需要将其放到事务外即可,例如:

csharp 复制代码
public void saveInfo (Params params, DTO dto) {
    query1(params);
    query2(params);
    transactionTemplate.execute(status -> {
      saveInfo(dto);
      return Boolean.TRUE;
    })
}

如果你继续想用声明式事务,那么可以通过下面的方式实现:

scss 复制代码
@Service
public class ServiceA {

  @Autowired
  private ServiceB serviceB;
  
  public void saveInfo(Params params, DTO dto) {
    query1(params);
    query2(params);
    serviceB.save(dto);
  }
}

@Service
public class ServiceB {
  
  @Transactional(rollbackFor = Exception.class)
  public void save(DTO dto) {
    save(dto);
  }
}

注: 这里需要注意的是,我并没有在ServiceA中创建save方法,原因也很简单,就是为了避免事务失效的问题。声明式事务@Transactional注解是通过spring aop起作用的,需要生成代理对象,而如果通过方法调用ServiceA中的save方法,使用的还是原始对象,事务是不会生效的。比较简单的做法就是新建一个ServiceB,并且将声明式事务注解加到ServiceB的save方法上。如果不想新建一个Service,那么你也可以通过获取当前对象的代理对象(AopContext.currentProxy())去调用,也可以在ServiceA中注入ServiceA,再通过调用save方法也是可以的。具体可以去了解一下有关@Transactional事务失效的几个场景。

事务中避免远程调用

在业务处理的接口中远程调用其它服务是比较正常的事情,由于受到网络波动的原因,远程调用的响应时间时间会比较长,也比较容易造成大事务的发生。针对这种情况,需要将远程调用放到事务外侧,通过自身重试以及兜底补偿等措施,实现最终一致性。

避免一次性处理过多数据

在数据量非常多的情况下,如果事务一次性处理的数据很多,会拉长事务的执行时间,通过分批处理的方式可以有效的解决该问题。例如有一批10000条数据需要更新,如果直接执行更新操作,会导致大量的数据锁等待,非常影响系统的正常业务处理。解决办法就是分批次处理,例如一次处理500条,分20次就能够处理完成(根据实际情况选择一次能处理多少数据)。这样做不仅能够减少大事务情况的出现,而且也不影响其它业务的正常处理。

异步

并不是所有的操作都需要同步进行,在业务允许的情况下,可以通过异步调用的方式进行处理,减少等待时间提升系统的吞吐量。使用异步处理,需要考虑的就是监控重试+兜底补偿,这一点必不可少。

总结

当你在面对大事务的问题时,可以采取上述的几种方案来解决。核心思想就是拆,既然是大事务,那么将他们拆成多个事务进行处理,不需要加入到事务中的操作就提出来,放到事务外侧。

如果这篇文章对您有帮助,帮忙点个免费的关注,求点赞、转发、在看,非常感谢! 今天的文章就写到这里了,我们下篇再会~~

相关推荐
阿祖zu13 分钟前
别再优化 RAG 了,适配 Agent 的 LLM Wiki 知识库理念
前端·后端·aigc
昵称为空C44 分钟前
手撸一个动态 SQL 执行引擎:不重启服务,在线增删改查任意数据库
spring boot·后端
用户8356290780511 小时前
用 Python 自动化 PowerPoint 演讲者备注添加
后端·python
神奇小汤圆1 小时前
科研神器再升级!Claude Code 全套 Skills,16 大科研场景全覆盖!
后端
tyung1 小时前
Go 手写有界 SPSC 环形队列:无 CAS、无锁、Cache 友好的无锁模型
后端·go
咕白m6251 小时前
使用 C# 在 Excel 中应用多种字体样式
后端·c#
Java编程爱好者1 小时前
放弃 Spring AI?这 3 个开源框架,才是让 SpringBoot 玩转 AI Agent 的正解
后端
二月龙1 小时前
伪类与伪元素深度解析:before/after 实用案例
后端
码事漫谈2 小时前
时序数据库2026盘点:国产数据库如何以“融合多模”走出差异化之路?
前端·后端
浮游本尊2 小时前
Java学习第42天 - Spring 事务传播、隔离级别、锁机制与并发一致性
后端