成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

麻了,這讓人絕望的大事務提交

開發(fā) 數(shù)據(jù)庫
在我們?nèi)粘i_發(fā)過程中,其實經(jīng)常會遇到大事務,本文將介紹大事務產(chǎn)生原因和影響以及優(yōu)化方案。

一、背景

繼上次的if else優(yōu)化也有段時間了,最近小貓又又又著道了,接手的那個項目又遇到了坑爹的地方,經(jīng)常性的報死鎖異常,經(jīng)常性的主從延遲......通過報錯信息按圖索驥,發(fā)現(xiàn)代碼是這樣的。

這是一段商品發(fā)布的邏輯,我們可以看到參數(shù)校驗、查詢、最終的insert以及update全部揉在一個事務中。遇到批量發(fā)布商品的時候就經(jīng)常出現(xiàn)問題了,數(shù)據(jù)庫主從延遲是肯定少不了的。

二、開啟優(yōu)化

其實像上述小貓遇到的這種狀況我們就稱其為大事務,那么我們就大概有這么一個定義。我們將執(zhí)行時間長,并且操作數(shù)據(jù)比較多的事務叫做大事務。

1.大事務產(chǎn)生的原因

在我們?nèi)粘i_發(fā)過程中,其實經(jīng)常會遇到大事務,老貓總結了一下,往往原因其實總結下來有這么幾點(當然存在紕漏的地方,也歡迎大家評論區(qū)留言補充):

  • 一次性操作的數(shù)據(jù)量確實多,大量的鎖競爭,比如批量操作這種行為。
  • 事務粒度過大,代碼中的 @Transactional使用不當,其他非DB操作比較多,耗時久。比如調(diào)用RPC接口,在例如上述小貓遇到的check邏輯甚至都揉在一起等等。

2.造成的影響

那么大事務造成的影響又是什么呢?

從開發(fā)者的角度來看的話,部分大事務必定對應的復雜的業(yè)務邏輯,代碼封裝事務拆解不合理,研發(fā)側維護困難,維護成本高。

從最終系統(tǒng)以及運維角度來看:

  • 出現(xiàn)了死鎖。
  • 造成了主從延遲。
  • 大事務消耗更多的磁盤空間,回滾成本高。
  • 大事務發(fā)生的過程中,由于連接池持續(xù)被打開,很容易造成數(shù)據(jù)庫連接池被沾滿。
  • 接口響應慢導致接口超時,甚至導致服務不可用等等 (歡迎大家補充)

3.優(yōu)化方案

大事務既然有這么多坑,那么我們來看一下我們?nèi)粘i_發(fā)過程中,應該如何做到盡量規(guī)避呢?老貓整理了以下幾種優(yōu)化方法。

a.降低事務顆粒度,大事務拆解小事務:

  • 編程式事務代替@Transactional。
  • 非update以及insert動作外移。

b.大數(shù)據(jù)量一次性提交盡可能拆解分批處理。

c.拆解原始事務,異步化處理。

(1) 降低事務顆粒度

1、我們對@Transactional的事務粒度把控不好,有時候如果使用不當?shù)脑捠聞展δ芸赡軙В绻?jīng)驗不足,很難排查,那么我們不如直接使用粗細粒度更好把控的編程式事務。TransactionTemplate。這樣的話咱們的優(yōu)化代碼就可以寫好才能如下方式。

@Autowired
private TransactionTemplate transactionTemplate;

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

非update以及insert動作外移。

原始代碼:

@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的情況,那直接這樣不就行了么。

錯誤改造案例:

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);
 }
}

這個例子是非常經(jīng)典的錯誤,這種直接方法調(diào)用的做法事務不會生效,老貓以前也踩過這樣的坑。因為 @Transactional 注解的聲明式事務是通過 spring aop 起作用的, 而 spring aop 需要生成代理對象,直接方法調(diào)用使用的還是原始對象,所以事務不會生效。那么我們應該如何改造呢?我們看下正確的改造。

正確改造方案1,當然還是利用上面的TransactionTemplate:

  @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方法上,把需要事務執(zhí)行的代碼移到新方法中。

  @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 再次注入到自身(老貓覺得這種方式不優(yōu)雅,不太推薦,這里就不寫了)

(2) 大數(shù)據(jù)量一次性提交盡可能拆解分批處理

我們再來看大數(shù)量批量請求的場景,咱們具體來分析一下,假設上游系統(tǒng)存在一個批量導入2w的數(shù)據(jù)操作。如果我們讀取到上游導入的數(shù)據(jù),并且直接執(zhí)行DB一次性執(zhí)行肯定是不合適的。這種情況就需要我們對其請求的數(shù)據(jù)量做一個拆解。我們可以采用Lists.partition等等方式將數(shù)據(jù)拆成多個小的批量然后再進行入庫操作處理。

@Servcie
public 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);
            }
        }
  }
}

@Servcie
public class ServiceB {
  @Transactional(rollbackFor=Exception.class)
  private void doInsertUpdate(List<InventorySkuDO> inventorySkuDOList){
        for (InventorySkuDO inventorySkuDO : inventorySkuDOList) {
           doInsert(inventorySkuDO);
           doUpdate(inventorySkuDO)
        }
  }
}

(3) 拆解原始事務,異步化處理

這種異步化處理的方案其實有兩種方式進行異步化操作。尤其是涉及到第三方RPC調(diào)用或者HTTP調(diào)用的時候,這種方案就更加適合。

方案一,采用CompletableFuture異步編排特性,當業(yè)務流程比較長的時候,我們可以將一個大業(yè)務拆解成多個小的任務進行異步化執(zhí)行。比如咱們有個批量支付的業(yè)務邏輯,因為整個流程是同步的,所以大概有了下面這樣的流程。(關于CompletableFeature老貓覺得挺有意思的,后續(xù)老貓會出專門的文章來理透該特性,歡迎大家持續(xù)關注)。

completeFeature

對應轉(zhuǎn)換成代碼邏輯的話,大概是這樣的:

void doBatchPay() {
        CompletableFuture<Object> task1 = CompletableFuture.supplyAsync(() -> {
            return "訂單信息";
        });
        CompletableFuture<Object> task2 = CompletableFuture.supplyAsync(() -> {
            try {
               return doPay();
            } catch (InterruptedException e) {
                //log add
            }
        });

        //task1、task2 執(zhí)行完執(zhí)行task3 ,需要感知task1和task2的執(zhí)行結果
        CompletableFuture<Object> future = task1.thenCombineAsync(task2, (t1, t2) -> {
            return "郵件發(fā)送成功";
        });
    }

方案二,Mq異步化處理,還是針對上述業(yè)務邏輯,我們是否可以將最終的發(fā)送郵件的動作剝離出來,最終再去統(tǒng)一執(zhí)行發(fā)送郵件。

關于偽代碼這里不展開了,有興趣的小伙伴可以自行實現(xiàn)一下。

責任編輯:趙寧寧 來源: 程序員老貓
相關推薦

2023-11-03 08:10:49

ThreadLoca內(nèi)存泄露

2020-11-30 14:40:52

事務系統(tǒng)項目

2023-11-21 20:28:02

C++Pythonweb開發(fā)

2021-12-26 21:49:19

微信面試參數(shù)

2020-09-19 16:56:09

人工智能應用人工智能AI

2024-03-04 08:29:33

數(shù)據(jù)定制化Java

2013-01-04 10:31:05

移動互聯(lián)網(wǎng)市場營銷CPM

2022-08-29 08:01:43

G1CMS回收器

2018-06-11 16:45:24

2023-12-01 10:19:00

接口優(yōu)化事務

2024-03-01 12:19:00

接口性能優(yōu)化

2009-08-14 09:19:15

Windows 7XP模式優(yōu)缺點

2015-06-17 17:03:32

微軟DockerAzure云

2015-04-01 11:29:01

創(chuàng)業(yè)創(chuàng)業(yè)公司考慮件事

2021-06-25 10:20:07

Linux技巧命令

2024-04-12 10:01:07

MySQL事務I/O

2022-12-19 08:23:24

2022-08-14 20:57:49

多線程代碼

2023-09-26 08:36:31

ES6 模塊JS 題目

2011-09-15 09:19:30

點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 在线观看中文字幕 | 91成人免费观看 | 久久综合久久综合久久综合 | 日韩在线观看中文字幕 | 91视频精选 | 国产精品二区三区 | 亚洲 欧美 精品 | 成人区精品一区二区婷婷 | 毛片视频免费观看 | 亚洲国产一区二区三区在线观看 | 一级毛片成人免费看a | 亚洲精品一区二区冲田杏梨 | 国产精品99久久久久久久久久久久 | 国产免费一区二区三区 | 91高清在线视频 | aaa在线观看| 国产美女福利在线观看 | 亚洲一区二区中文字幕在线观看 | 久久欧美高清二区三区 | 亚洲区一区二 | 高清欧美性猛交 | 日日干干| 国产三级国产精品 | 欧美伦理一区 | 一级美国黄色片 | 日日爱av | 亚洲视频在线观看 | 免费黄色片在线观看 | 色精品| 亚洲综合第一页 | 久久久精品久 | 男女午夜免费视频 | 奇米视频777 | 欧美xxxx日本 | 久久精品性视频 | 欧美日韩一区二区电影 | 欧美一区二区三区在线看 | 久久久久中文字幕 | 免费毛片网站在线观看 | 欧美一级二级视频 | 能看的av网站 |