当前位置:首页 > 科技  > 软件

Spring事务长了个腿?轻松掌握技巧告别长事务烦恼!

来源: 责编: 时间:2024-01-02 09:31:37 313观看
导读大家好,我是飘渺。今天继续DDD&微服务专栏。在之前的文章 基于DDD的订单创建 流程中,我们留下了一个问题:在createOrder()方法中,我将调用远程接口获取购物车详情、远程库存校验、订单保存放在一个事务中,显然这并不是一个

大家好,我是飘渺。今天继续DDD&微服务专栏。5s528资讯网——每日最新资讯28at.com

在之前的文章 基于DDD的订单创建 流程中,我们留下了一个问题:在createOrder()方法中,我将调用远程接口获取购物车详情、远程库存校验、订单保存放在一个事务中,显然这并不是一个正确的做法,因为它会导致长事务,今天就让我们来解决这个问题。5s528资讯网——每日最新资讯28at.com

图片图片5s528资讯网——每日最新资讯28at.com

为什么会产生长事务

首先,让我们来分析一下产生长事务的原因。5s528资讯网——每日最新资讯28at.com

在Spring中,@Transactional注解是基于AOP实现的,本质上是在目标方法执行前后进行拦截。在目标方法执行前加入或创建一个事务,在方法执行后,根据实际情况选择提交或回滚事务。5s528资讯网——每日最新资讯28at.com

当Spring遇到该注解时,会自动从数据库连接池中获取连接并开启事务,然后绑定到ThreadLocal上,对于@Transactional注解包裹的整个方法都是使用同一个连接。如果出现耗时的操作,如第三方接口调用、业务逻辑复杂、大批量数据处理等,就会导致占用连接的时间很长,数据库连接一直被占用不释放。一旦类似操作过多,就会导致数据库连接池耗尽。5s528资讯网——每日最新资讯28at.com

在开头的实例中,一个事务中执行RPC操作是典型的长事务问题。类似的操作还包括在事务中进行大量数据查询、业务规则处理等。5s528资讯网——每日最新资讯28at.com

长事务会产生哪些问题

长事务引发的常见危害有:5s528资讯网——每日最新资讯28at.com

  1. 数据库连接池被占满,应用无法获取连接资源;
  2. 容易引发数据库死锁;
  3. 数据库回滚时间长;
  4. 在主从架构中会导致主从延时变大。

如何避免长事务

既然知道了长事务的危害,那么在开发中如何避免这个问题呢?5s528资讯网——每日最新资讯28at.com

很明显,解决长事务的宗旨就是 对事务方法进行拆分,尽量让事务变小,变快,减小事务的颗粒度。5s528资讯网——每日最新资讯28at.com

编程式事务

因此,我们可以采用编程式事务替代声明式事务@Transactional。在Spring项目中,可以注入TransactionTemplate对象,然后手动控制事务范围。改造过后的代码如下所示:5s528资讯网——每日最新资讯28at.com

public String createOrder(OrderCreateRequest orderCreateRequest) { // 获取购物车详情 ShoppingCartDetailDTO shoppingCartDetailDTO = cartRemoteFacade.queryCheckedCartItemByUserId(orderCreateRequest.getCustomerId()); List<CartItemDTO> cartItemList = shoppingCartDetailDTO.getCartItemDtoS(); //校验库存 checkInventory(cartItemList); ...  transactionTemplate.executeWithoutResult(status -> {       orderRepository.save(tradeOrder);  eventPublisher.publishEvent(new OrderCreatedEvent(tradeOrder));  }); return orderSn;}

然而,这里涉及到另一个问题:在保存订单后我们通过EventPublisher发布了一个事件,让监听者来处理剩下的业务逻辑,(在Dailymart中创建订单后需要进行库存预扣),在默认情况下,Spring的事件监听机制是同步的将代码进行解耦,我们希望库存扣减如果出现失败需要回滚订单,而编程式事务无法控制监听者的事务。因此,在这种场景下并不适合使用编程式事务来处理。5s528资讯网——每日最新资讯28at.com

敲黑板:使用编程式事务替代声明式事务是解决长事务最简单的实现方式,在大部分场景下都可以采用。在使用时要注意编程式事务搭配EventPublisher时无法控制监听者的事务。5s528资讯网——每日最新资讯28at.com

对方法进行拆分

另外一种常见的处理措施就是将方法进行拆分,将大方法拆成小方法,将不需要事务管理的逻辑与事务操作拆开。5s528资讯网——每日最新资讯28at.com

public String createOrder(OrderCreateRequest orderCreateRequest) { // 获取购物车详情 ShoppingCartDetailDTO shoppingCartDetailDTO = cartRemoteFacade.queryCheckedCartItemByUserId(orderCreateRequest.getCustomerId()); List<CartItemDTO> cartItemList = shoppingCartDetailDTO.getCartItemDtoS(); //校验库存 checkInventory(cartItemList); ...  this.saveOrder(tradeOrder) return orderSn;}@Transactional(rollbackFor = RuntimeException.class)private void saveOrder(TradeOrder tradeOrder){ orderRepository.save(tradeOrder); eventPublisher.publishEvent(new OrderCreatedEvent(tradeOrder)); }

在上述代码中,获取购物车详情与库存校验不需要事务,将其与事务方法saveOrder()分开。然而,这样的简单拆分会导致事务不生效。这又涉及到另一个知识点:5s528资讯网——每日最新资讯28at.com

@Transactional注解的声明式事务是通过spring aop起作用的,而spring aop需要生成代理对象,直接在同一个类中方法调用使用的还是原始对象,事务不生效。其他几个常见的事务不生效的场景为:5s528资讯网——每日最新资讯28at.com

  • @Transactional 应用在非 public 修饰的方法上
  • @Transactional 注解属性 propagation 设置错误
  • @Transactional 注解属性 rollbackFor 设置错误
  • 同一个类中方法调用,导致@Transactional失效
  • 异常被catch捕获导致@Transactional失效

正确的拆分方法应该使用下面两种:5s528资讯网——每日最新资讯28at.com

  1. 将方法放入另一个类,如新增一个Manager层,通过Spring注入,这样符合了在对象之间调用的条件。详细说明可以参考我的文章为什么阿里建议给MVC三层架构再加一层Manager层!。
  2. 启动类添加@EnableAspectJAutoProxy(exposeProxy = true),方法内使用AopContext.currentProxy()获得代理类,使用事务。
SpringBootApplication.java    @EnableAspectJAutoProxy(exposeProxy = true)  @SpringBootApplication  public class SpringBootApplication {}public String createOrder(OrderCreateRequest orderCreateRequest) { ... OrderService orderService = (OrderService)AopContext.currentProxy();     orderService.saveData(tradeOrder);   return orderSn;}@Transactional(rollbackFor = RuntimeException.class)private void saveOrder(TradeOrder tradeOrder){ orderRepository.save(tradeOrder); eventPublisher.publishEvent(new OrderCreatedEvent(tradeOrder)); }

然而,Dailymart项目是基于DDD的分层架构模型实现。原来的业务逻辑是在应用服务编写,在我们项目中只需要将保存订单的逻辑放在领域服务层,由领域服务保证事务,而应用服务层负责组装业务逻辑。最终代码如下:5s528资讯网——每日最新资讯28at.com

private final TradeOrderService tradeOrderService;@Override  // @Transactional(rollbackFor = RuntimeException.class)  public String createOrder(OrderCreateRequest orderCreateRequest) {      // 生成订单编号      String orderSn = IdUtils.nextIdStr();      // 获取购物车详情      ShoppingCartDetailDTO shoppingCartDetailDTO = cartRemoteFacade.queryCheckedCartItemByUserId(orderCreateRequest.getCustomerId());      List<CartItemDTO> cartItemList = shoppingCartDetailDTO.getCartItemDtoS();            // 校验库存      checkInventory(cartItemList);   ...          tradeOrderService.save(tradeOrder);     return orderSn;  }@Service  @RequiredArgsConstructor(onConstructor = @__(@Autowired))  public class TradeOrderServiceImpl implements TradeOrderService {            private final ApplicationEventPublisher eventPublisher;            private final OrderRepository orderRepository;            @Override      @Transactional        public void save(TradeOrder tradeOrder) {          orderRepository.save(tradeOrder);          eventPublisher.publishEvent(new OrderCreatedEvent(tradeOrder));      }}

小结

本文讨论了长事务的危害及解决方案。首先,我们探讨了长事务导致的问题,包括数据库连接池耗尽、死锁等。其次,介绍了两种解决策略:采用编程式事务和对方法进行拆分。编程式事务提供了手动控制事务范围的方式,但需要注意搭配EventPublisher可能导致监听者事务无法控制的问题。对方法进行拆分是一种更通用的方法,能够减小事务范围,提高执行效率。最后,通过实际的DDD分层架构示例,展示了在应用服务层和领域服务层中如何组织业务逻辑,确保事务正确性和性能。5s528资讯网——每日最新资讯28at.com

本文链接:http://www.28at.com/showinfo-26-55265-0.htmlSpring事务长了个腿?轻松掌握技巧告别长事务烦恼!

声明:本网页内容旨在传播知识,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。邮件:2376512515@qq.com

上一篇: 浅谈ArkUI之Web组件的基础用法

下一篇: Spring事务长了个腿?轻松掌握技巧告别长事务烦恼!

标签:
  • 热门焦点
  • 小米降噪蓝牙耳机Necklace分享:听一首歌 读懂一个故事

    在今天下午的小米Civi 2新品发布会上,小米还带来了一款新的降噪蓝牙耳机Necklace,我们也在发布结束的第一时间给大家带来这款耳机的简单分享。现在大家能见到最多的蓝牙耳机
  • 2023 年的 Node.js 生态系统

    随着技术的不断演进和创新,Node.js 在 2023 年达到了一个新的高度。Node.js 拥有一个庞大的生态系统,可以帮助开发人员更快地实现复杂的应用。本文就来看看 Node.js 最新的生
  • Java NIO内存映射文件:提高文件读写效率的优秀实践!

    Java的NIO库提供了内存映射文件的支持,它可以将文件映射到内存中,从而可以更快地读取和写入文件数据。本文将对Java内存映射文件进行详细的介绍和演示。内存映射文件概述内存
  • Flowable工作流引擎的科普与实践

    一.引言当我们在日常工作和业务中需要进行各种审批流程时,可能会面临一系列技术和业务上的挑战。手动处理这些审批流程可能会导致开发成本的增加以及业务复杂度的上升。在这
  • .NET 程序的 GDI 句柄泄露的再反思

    一、背景1. 讲故事上个月我写过一篇 如何洞察 C# 程序的 GDI 句柄泄露 文章,当时用的是 GDIView + WinDbg 把问题搞定,前者用来定位泄露资源,后者用来定位泄露代码,后面有朋友反
  • 雅柏威士忌多款单品价格大跌,泥煤顶流也不香了?

    来源 | 烈酒商业观察编 | 肖海林今年以来,威士忌市场开始出现了降温迹象,越来越多不断暴涨的网红威士忌也开始悄然回归市场理性。近日,LVMH集团旗下苏格兰威士忌品牌雅柏(Ardbeg
  • 新电商三兄弟,“抖快红”成团!

    来源:价值研究所作 者:Hernanderz 随着内容电商的概念兴起,抖音、快手、小红书组成的&ldquo;新电商三兄弟&rdquo;成为业内一股不可忽视的势力,给阿里、京东、拼多多带去了巨大压
  • 滴滴违法违规被罚80.26亿 共存在16项违法事实

    滴滴违法违规被罚80.26亿 存在16项违法事实开始于2121年7月,历经一年时间,网络安全审查办公室对“滴滴出行”网络安全审查终于有了一个暂时的结束。据“网信
  • 联想的ThinkBook Plus下一版曝光,键盘旁边塞个平板

    ThinkBook Plus 是联想的一个特殊笔记本类别,它在封面放入了一块墨水屏,也给人留下了较为深刻的印象。据有人爆料,联想的下一款 ThinkBook Plus 可能更特殊,它
Top