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

麻了,这让人绝望的大事务提交

来源: 责编: 时间:2024-01-15 09:20:19 262观看
导读一、背景继上次的if else优化也有段时间了,最近小猫又又又着道了,接手的那个项目又遇到了坑爹的地方,经常性的报死锁异常,经常性的主从延迟......通过报错信息按图索骥,发现代码是这样的。这是一段商品发布的逻辑,我们可以

一、背景

继上次的if else优化也有段时间了,最近小猫又又又着道了,接手的那个项目又遇到了坑爹的地方,经常性的报死锁异常,经常性的主从延迟......通过报错信息按图索骥,发现代码是这样的。NYT28资讯网——每日最新资讯28at.com

NYT28资讯网——每日最新资讯28at.com

NYT28资讯网——每日最新资讯28at.com

这是一段商品发布的逻辑,我们可以看到参数校验、查询、最终的insert以及update全部揉在一个事务中。遇到批量发布商品的时候就经常出现问题了,数据库主从延迟是肯定少不了的。NYT28资讯网——每日最新资讯28at.com

二、开启优化

其实像上述小猫遇到的这种状况我们就称其为大事务,那么我们就大概有这么一个定义。我们将执行时间长,并且操作数据比较多的事务叫做大事务。NYT28资讯网——每日最新资讯28at.com

1.大事务产生的原因

在我们日常开发过程中,其实经常会遇到大事务,老猫总结了一下,往往原因其实总结下来有这么几点(当然存在纰漏的地方,也欢迎大家评论区留言补充):NYT28资讯网——每日最新资讯28at.com

  • 一次性操作的数据量确实多,大量的锁竞争,比如批量操作这种行为。
  • 事务粒度过大,代码中的 @Transactional使用不当,其他非DB操作比较多,耗时久。比如调用RPC接口,在例如上述小猫遇到的check逻辑甚至都揉在一起等等。

2.造成的影响

那么大事务造成的影响又是什么呢?NYT28资讯网——每日最新资讯28at.com

从开发者的角度来看的话,部分大事务必定对应的复杂的业务逻辑,代码封装事务拆解不合理,研发侧维护困难,维护成本高。NYT28资讯网——每日最新资讯28at.com

从最终系统以及运维角度来看:NYT28资讯网——每日最新资讯28at.com

  • 出现了死锁。
  • 造成了主从延迟。
  • 大事务消耗更多的磁盘空间,回滚成本高。
  • 大事务发生的过程中,由于连接池持续被打开,很容易造成数据库连接池被沾满。
  • 接口响应慢导致接口超时,甚至导致服务不可用等等 (欢迎大家补充)

3.优化方案

大事务既然有这么多坑,那么我们来看一下我们日常开发过程中,应该如何做到尽量规避呢?老猫整理了以下几种优化方法。NYT28资讯网——每日最新资讯28at.com

a.降低事务颗粒度,大事务拆解小事务:NYT28资讯网——每日最新资讯28at.com

  • 编程式事务代替@Transactional。
  • 非update以及insert动作外移。

b.大数据量一次性提交尽可能拆解分批处理。NYT28资讯网——每日最新资讯28at.com

c.拆解原始事务,异步化处理。NYT28资讯网——每日最新资讯28at.com

(1) 降低事务颗粒度

1、我们对@Transactional的事务粒度把控不好,有时候如果使用不当的话事务功能可能会失效,如果经验不足,很难排查,那么我们不如直接使用粗细粒度更好把控的编程式事务。TransactionTemplate。这样的话咱们的优化代码就可以写好才能如下方式。NYT28资讯网——每日最新资讯28at.com

@Autowiredprivate TransactionTemplate transactionTemplate;public boolean publishProduct(PublishProductRequest request) {        externalSellerAuthorizeService.checkAuthorizeValid(request.getSellerId(),request.getThirdCategoryId(),request.getBrandId());        ......        transactionTemplate.execute((status) -> {            try{                //执行insert                productDao.insert(productDO);                productDescDao.insert(productDescDO);                ....                //其他insert以及update操作            }catch (Exception e) {                //回滚                status.setRollbackOnly();                return true;            }            return false;        });        return true;    }

非update以及insert动作外移。NYT28资讯网——每日最新资讯28at.com

原始代码:NYT28资讯网——每日最新资讯28at.com

@Transactional(rollbackFor=Exception.class)   public void save(Req req) {         checkParam(req);         saveData1(req);         updateData2(req);   }   private void checkParam(Req req){       Data1 data = selectData1();       Data2 data2 = selectData2();       if(data.getSomeThing() != STATUS_YES){          throw new BusinessTimeException(.....);       }   }

然后部分小伙伴就觉得外移么,如果不用@Transactional的情况,那直接这样不就行了么。NYT28资讯网——每日最新资讯28at.com

错误改造案例:NYT28资讯网——每日最新资讯28at.com

class ServiceAImpl implements ServiceA {  @Transactional(rollbackFor=Exception.class)   public void save(Req req) {         saveData1(req);         updateData2(req);   }   private void checkParam(Req req){       Data1 data = selectData1();       Data2 data2 = selectData2();       if(data.getSomeThing() != STATUS_YES){          throw new BusinessTimeException(.....);       }   } public void save(Req req){    checkParam(req);    doSave(req); }}

这个例子是非常经典的错误,这种直接方法调用的做法事务不会生效,老猫以前也踩过这样的坑。因为 @Transactional 注解的声明式事务是通过 spring aop 起作用的, 而 spring aop 需要生成代理对象,直接方法调用使用的还是原始对象,所以事务不会生效。那么我们应该如何改造呢?我们看下正确的改造。NYT28资讯网——每日最新资讯28at.com

正确改造方案1,当然还是利用上面的TransactionTemplate:NYT28资讯网——每日最新资讯28at.com

  @Autowired  private TransactionTemplate transactionTemplate;   public void save(Req req) {         checkParam(req);         transactionTemplate.execute((status) -> {            try{                saveData1(req);                updateData2(req);                ....                //其他insert以及update操作            }catch (Exception e) {                //回滚                status.setRollbackOnly();                return true;            }            return false;        });   }   private void checkParam(Req req){       Data1 data = selectData1();       Data2 data2 = selectData2();       if(data.getSomeThing() != STATUS_YES){          throw new BusinessTimeException(.....);       }   }

正确改造方案2,把 @Transactional 注解加到新Service方法上,把需要事务执行的代码移到新方法中。NYT28资讯网——每日最新资讯28at.com

  @Servcie  public class ServiceA {     @Autowired     private ServiceB serviceB;     private void checkParam(Req req){       Data1 data = selectData1();       Data2 data2 = selectData2();       if(data.getSomeThing() != STATUS_YES){          throw new BusinessTimeException(.....);       }   }    public void save(Req req) {          checkParam(req);          serviceB.save(req);    }  }   @Servcie   public class ServiceB {      @Transactional(rollbackFor=Exception.class)      public void save(Req req) {         saveData1(req);         updateData2(req);      }   }

正确改造方案3:将ServiceA 再次注入到自身(老猫觉得这种方式不优雅,不太推荐,这里就不写了)NYT28资讯网——每日最新资讯28at.com

(2) 大数据量一次性提交尽可能拆解分批处理

我们再来看大数量批量请求的场景,咱们具体来分析一下,假设上游系统存在一个批量导入2w的数据操作。如果我们读取到上游导入的数据,并且直接执行DB一次性执行肯定是不合适的。这种情况就需要我们对其请求的数据量做一个拆解。我们可以采用Lists.partition等等方式将数据拆成多个小的批量然后再进行入库操作处理。NYT28资讯网——每日最新资讯28at.com

@Servciepublic class ServiceA {  @Autowired  private ServiceB serviceB;  private void batchAdd(List<Long> inventorySkuIdList){      List<List<Long>> partition = Lists.partition(inventorySkuIdList, 1000);        for (List<Long> idList : partition) {            List<InventorySkuDO> inventorySkuDOList = inventorySkuDao.selectByIdList(idList, null);            if (CollectionUtils.isNotEmpty(inventorySkuDOList)) {               serviceB.doInsertUpdate(inventorySkuDOList);            }        }  }}@Servciepublic class ServiceB {  @Transactional(rollbackFor=Exception.class)  private void doInsertUpdate(List<InventorySkuDO> inventorySkuDOList){        for (InventorySkuDO inventorySkuDO : inventorySkuDOList) {           doInsert(inventorySkuDO);           doUpdate(inventorySkuDO)        }  }}

(3) 拆解原始事务,异步化处理

这种异步化处理的方案其实有两种方式进行异步化操作。尤其是涉及到第三方RPC调用或者HTTP调用的时候,这种方案就更加适合。NYT28资讯网——每日最新资讯28at.com

方案一,采用CompletableFuture异步编排特性,当业务流程比较长的时候,我们可以将一个大业务拆解成多个小的任务进行异步化执行。比如咱们有个批量支付的业务逻辑,因为整个流程是同步的,所以大概有了下面这样的流程。(关于CompletableFeature老猫觉得挺有意思的,后续老猫会出专门的文章来理透该特性,欢迎大家持续关注)。NYT28资讯网——每日最新资讯28at.com

NYT28资讯网——每日最新资讯28at.com

completeFeatureNYT28资讯网——每日最新资讯28at.com

对应转换成代码逻辑的话,大概是这样的:NYT28资讯网——每日最新资讯28at.com

void doBatchPay() {        CompletableFuture<Object> task1 = CompletableFuture.supplyAsync(() -> {            return "订单信息";        });        CompletableFuture<Object> task2 = CompletableFuture.supplyAsync(() -> {            try {               return doPay();            } catch (InterruptedException e) {                //log add            }        });        //task1、task2 执行完执行task3 ,需要感知task1和task2的执行结果        CompletableFuture<Object> future = task1.thenCombineAsync(task2, (t1, t2) -> {            return "邮件发送成功";        });    }

方案二,Mq异步化处理,还是针对上述业务逻辑,我们是否可以将最终的发送邮件的动作剥离出来,最终再去统一执行发送邮件。NYT28资讯网——每日最新资讯28at.com

NYT28资讯网——每日最新资讯28at.com

关于伪代码这里不展开了,有兴趣的小伙伴可以自行实现一下。NYT28资讯网——每日最新资讯28at.com

本文链接:http://www.28at.com/showinfo-26-60936-0.html麻了,这让人绝望的大事务提交

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

上一篇: 我们优雅判断 interface 是否为 nil

下一篇: 为啥我放弃用 Pinia 和 Vuex 去做字典状态管理呢?

标签:
  • 热门焦点
  • Rust中的高吞吐量流处理

    作者 | Noz编译 | 王瑞平本篇文章主要介绍了Rust中流处理的概念、方法和优化。作者不仅介绍了流处理的基本概念以及Rust中常用的流处理库,还使用这些库实现了一个流处理程序
  • Automa-通过连接块来自动化你的浏览器

    1、前言通过浏览器插件可实现自动化脚本的录制与编写,具有代表性的工具就是:Selenium IDE、Katalon Recorder,对于简单的业务来说可快速实现自动化的上手工作。Selenium IDEKat
  • 三言两语说透设计模式的艺术-单例模式

    写在前面单例模式是一种常用的软件设计模式,它所创建的对象只有一个实例,且该实例易于被外界访问。单例对象由于只有一个实例,所以它可以方便地被系统中的其他对象共享,从而减少
  • 量化指标是与非:挽救被量化指标扼杀的技术团队

    作者 | 刘新翠整理 | 徐杰承本文整理自快狗打车技术总监刘新翠在WOT2023大会上的主题分享,更多精彩内容及现场PPT,请关注51CTO技术栈公众号,发消息【WOT2023PPT】即可直接领取
  • 一文搞定Java NIO,以及各种奇葩流

    大家好,我是哪吒。很多朋友问我,如何才能学好IO流,对各种流的概念,云里雾里的,不求甚解。用到的时候,现百度,功能虽然实现了,但是为什么用这个?不知道。更别说效率问题了~下次再遇到,
  • 本地生活这块肥肉,拼多多也想吃一口

    出品/壹览商业 作者/李彦编辑/木鱼拼多多也看上本地生活这块蛋糕了。近期,拼多多在App首页&ldquo;充值中心&rdquo;入口上线了本机生活界面。壹览商业发现,该界面目前主要
  • 华为HarmonyOS 4升级计划公布:首批34款机型今日开启公测

    8月4日消息,今天下午华为正式发布了HarmonyOS 4系统,在更流畅的前提下,还带来了不少新功能,UI设计也有变化,会让手机焕然一新。华为宣布,首批机型将会在
  • 苹果公司要求三星和LG Display生产「无边框」OLED iPhone显示屏

    据 The Elec 报道,苹果已要求其供应商为未来的 iPhone 型号开发「无边框」OLED 显示面板。苹果显然已要求三星和 LG Display 开发新的 OLED 显示面
  • 三翼鸟智能家居亮相电博会,让用户体验更真实

    2021电博会在青岛国际会展中心开幕中,三翼鸟直接把“家”搬到了现场,成为了展会的一大看点。这也是三翼鸟继9月9日发布了行业首个一站式定制智慧家平台后的
Top