自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

性能提升了200%?。▋?yōu)化篇)

開發(fā) 架構(gòu)
由于這個(gè)數(shù)據(jù)校對(duì)系統(tǒng)最初不是我開發(fā)的,我了解了下數(shù)據(jù)校對(duì)系統(tǒng)的業(yè)務(wù),整體來說,數(shù)據(jù)校對(duì)系統(tǒng)的業(yè)務(wù)還是比較簡(jiǎn)單的。用戶通過商城提交訂單后,會(huì)在訂單微服務(wù)中生成訂單信息,保存在訂單數(shù)據(jù)庫(kù)中。

 [[414791]]

最近不少運(yùn)營(yíng)同事找到我說:咱們的數(shù)據(jù)校對(duì)系統(tǒng)越來越慢了,要過很久才會(huì)顯示出校對(duì)結(jié)果,你能不能快速優(yōu)化一下呢?我:好的,我先了解下業(yè)務(wù),后續(xù)優(yōu)化下。

優(yōu)化背景

由于這個(gè)數(shù)據(jù)校對(duì)系統(tǒng)最初不是我開發(fā)的,我了解了下數(shù)據(jù)校對(duì)系統(tǒng)的業(yè)務(wù),整體來說,數(shù)據(jù)校對(duì)系統(tǒng)的業(yè)務(wù)還是比較簡(jiǎn)單的。用戶通過商城提交訂單后,會(huì)在訂單微服務(wù)中生成訂單信息,保存在訂單數(shù)據(jù)庫(kù)中。訂單微服務(wù)會(huì)調(diào)用庫(kù)存微服務(wù)的接口,扣減商品的庫(kù)存數(shù)量,并且會(huì)將每筆訂單扣減庫(kù)存的記錄保存在庫(kù)存數(shù)據(jù)庫(kù)中。為了防止用戶提交訂單后沒有扣減庫(kù)存,或者重復(fù)扣減庫(kù)存,數(shù)據(jù)校對(duì)系統(tǒng)每天會(huì)校驗(yàn)訂單中提交的商品數(shù)量與扣減的庫(kù)存數(shù)量是否一致,并且會(huì)將校對(duì)的結(jié)果信息保存到數(shù)據(jù)校對(duì)信息表中。

數(shù)據(jù)校對(duì)系統(tǒng)的總體流程為:先查詢訂單記錄,然后在查詢庫(kù)存的扣減記錄,然后對(duì)比訂單和庫(kù)存扣減記錄,然后將校對(duì)的結(jié)果信息保存到數(shù)據(jù)校對(duì)信息表中,整體流程如下所示。

為了能夠讓大家更好的了解數(shù)據(jù)校對(duì)系統(tǒng)對(duì)于訂單和庫(kù)存的校對(duì)業(yè)務(wù),我將代碼精簡(jiǎn)了下,核心業(yè)務(wù)邏輯代碼如下所示。

  1. //檢測(cè)是否存在未對(duì)賬訂單 
  2. checkOrders = checkOrders(); 
  3. while(checkOrders != null){ 
  4.     //查詢未校對(duì)的訂單信息 
  5.     hasNoOrders = getHasNoOrders(); 
  6.     //查詢未校對(duì)的庫(kù)存記錄 
  7.     hasNoStock = getHasNoStock(); 
  8.     //校對(duì)數(shù)據(jù)并返回結(jié)果 
  9.     checkResult = checkData(hasNoOrders, hasNoStock); 
  10.     //將結(jié)果信息保存到數(shù)據(jù)校對(duì)信息表中 
  11.     saveCheckResult(checkResult); 
  12.     //檢測(cè)是否存在未對(duì)賬訂單 
  13.     checkOrders = checkOrders(); 

好了,上述就是系統(tǒng)優(yōu)化的背景,想必看到這里,很多小伙伴應(yīng)該知道問題出在哪里了。我們繼續(xù)往下看。

問題分析

雖然很多小伙伴應(yīng)該已經(jīng)知道系統(tǒng)性能低下的問題所在了,這里,我們就一起詳細(xì)分析下校對(duì)系統(tǒng)性能低下的原因。

既然運(yùn)營(yíng)的同事說數(shù)據(jù)校對(duì)系統(tǒng)越來越慢了,我們首先要做的就是找到系統(tǒng)的性能瓶頸所在。據(jù)了解,目前的數(shù)據(jù)對(duì)賬系統(tǒng),由于訂單記錄和庫(kù)存扣減記錄數(shù)據(jù)量巨大,所以查詢未校對(duì)的訂單信息的方法getHasNoOrders()和查詢?yōu)樾?duì)的庫(kù)存記錄的方法getHasNoStock()相對(duì)來說比較慢。并且在數(shù)據(jù)校對(duì)系統(tǒng)中,校對(duì)訂單和庫(kù)存記錄的方法是單線程執(zhí)行的,我們可以簡(jiǎn)單畫一個(gè)時(shí)間抽線圖,如下所示。

由圖可以看出,以單線程的方式getHasNoOrders()方法和getHasNoStock()方法耗費(fèi)了大量的時(shí)間,這兩個(gè)方法本身在邏輯上就是兩個(gè)獨(dú)立的方法,并且這兩個(gè)方法沒有先后的執(zhí)行的順序依賴。那這兩個(gè)方法能不能并行執(zhí)行呢?很顯然是可以的。那我們把getHasNoOrders()方法和getHasNoStock()方法分別放到兩個(gè)不同的線程中,優(yōu)化下系統(tǒng)的性能,整體流程如下所示。

優(yōu)化后,我們將getHasNoOrders()方法放到線程1中執(zhí)行,getHasNoStock()方法放到線程2中執(zhí)行,checkData()方法和saveCheckResult()方法發(fā)放到線程3中執(zhí)行,優(yōu)化后的系統(tǒng)性能相比優(yōu)化前的系統(tǒng)性能幾乎提升了一倍,優(yōu)化效果相對(duì)來說還是比較明顯的。

說到這里,大家應(yīng)該應(yīng)該知道具體怎么優(yōu)化了吧?好,我們繼續(xù)往下看!

解決方案

解決問題的思路有了,接下來,我們看看如何使用代碼實(shí)現(xiàn)我們上面分析的解決問題的思路。這里,我們可以分別開啟兩個(gè)線程執(zhí)行g(shù)etHasNoOrders()方法和getHasNoStock()方法,在主線程中執(zhí)行checkData()方法和saveCheckResult()方法。這里需要注意的是:主線程需要等待兩個(gè)子線程執(zhí)行完畢之后再執(zhí)行checkData()方法和saveCheckResult()方法。 為了實(shí)現(xiàn)這個(gè)功能,我們可以使用Thread類中join()方法,有關(guān)Thread類中join()方法的具體說明,這里,具體的邏輯就是在主線程中調(diào)用兩個(gè)子線程的join()方法實(shí)現(xiàn)阻塞等待,當(dāng)兩個(gè)子線程執(zhí)行完畢退出時(shí),調(diào)用兩個(gè)子線程join()方法的主線程會(huì)被喚醒,從而執(zhí)行主線程中的checkData()方法和saveCheckResult()方法。大體代碼如下所示。

  1. //檢測(cè)是否存在未對(duì)賬訂單 
  2. checkOrders = checkOrders(); 
  3. while(checkOrders != null){ 
  4.     Thread t1 = new Thread(()->{ 
  5.         //查詢未校對(duì)的訂單信息 
  6.         hasNoOrders = getHasNoOrders(); 
  7.     }); 
  8.     t1.start(); 
  9.     Thread t2 = new Thread(()->{ 
  10.        //查詢未校對(duì)的庫(kù)存記錄 
  11.        hasNoStock = getHasNoStock(); 
  12.     }); 
  13.      t2.start(); 
  14.     //阻塞主線程,等待線程t1和線程t2執(zhí)行完畢 
  15.     t1.join(); 
  16.     t2.join(); 
  17.     //校對(duì)數(shù)據(jù)并返回結(jié)果 
  18.     checkResult = checkData(hasNoOrders, hasNoStock); 
  19.     //將結(jié)果信息保存到數(shù)據(jù)校對(duì)信息表中 
  20.     saveCheckResult(checkResult); 
  21.     //檢測(cè)是否存在未對(duì)賬訂單 
  22.     checkOrders = checkOrders(); 

至此,我們基本上能夠解決問題了。但是,還有沒有進(jìn)一步優(yōu)化的空間呢?我們進(jìn)一步往下看。

進(jìn)一步優(yōu)化

通過上面對(duì)系統(tǒng)優(yōu)化,基本能夠達(dá)成我們的優(yōu)化目標(biāo),但是上面的解決方案存在著不足的地方,那就是在while循環(huán)里每次都要新建兩個(gè)線程分別執(zhí)行g(shù)etHasNoOrders()方法和getHasNoStock()方法,了解Java多線程的小伙伴們應(yīng)該都知道,在Java中創(chuàng)建線程可是個(gè)非常耗時(shí)的操作。所以,最好是能夠?qū)?chuàng)建出來的線程反復(fù)使用。這里,估計(jì)很多小伙伴都會(huì)想到使用線程池,沒錯(cuò),我們可以使用線程池進(jìn)一步優(yōu)化上面的代碼。

遇到新的問題

不過在使用線程池進(jìn)一步優(yōu)化時(shí),我們會(huì)遇到一個(gè)問題,就是主線程如何等待子線程中的結(jié)果數(shù)據(jù)呢?說直白點(diǎn)就是:主線程如何知道子線程中的getHasNoOrders()方法和getHasNoStock()方法執(zhí)行完了? 由于在之前的代碼中我們是在主線程中調(diào)用子線程的join()方法等待子線程執(zhí)行完畢,獲取到子線程執(zhí)行的結(jié)果后,繼續(xù)執(zhí)行主線程的邏輯。但是如果使用了線程池的話,線程池中的線程根本不會(huì)退出,此時(shí),我們無法使用線程的join()方法等待線程執(zhí)行完畢。

所以,主線程如何知道子線程中的getHasNoOrders()方法和getHasNoStock()方法執(zhí)行完了? 這個(gè)問題就成了關(guān)鍵的突破點(diǎn)。這里,我們使用線程池進(jìn)一步優(yōu)化的代碼如下所示。

  1. //檢測(cè)是否存在未對(duì)賬訂單 
  2. checkOrders = checkOrders(); 
  3. //創(chuàng)建線程池 
  4. Executor executor =  Executors.newFixedThreadPool(2); 
  5. while(checkOrders != null){ 
  6.     executor.execute(()->{ 
  7.         //查詢未校對(duì)的訂單信息 
  8.         hasNoOrders = getHasNoOrders(); 
  9.     }); 
  10.     executor.execute(()->{ 
  11.        //查詢未校對(duì)的庫(kù)存記錄 
  12.        hasNoStock = getHasNoStock(); 
  13.     }); 
  14.      
  15.     /**如何知道子線程中的getHasNoOrders()方法和getHasNoStock()方法執(zhí)行完了成為關(guān)鍵**/ 
  16.      
  17.     //校對(duì)數(shù)據(jù)并返回結(jié)果 
  18.     checkResult = checkData(hasNoOrders, hasNoStock); 
  19.     //將結(jié)果信息保存到數(shù)據(jù)校對(duì)信息表中 
  20.     saveCheckResult(checkResult); 
  21.     //檢測(cè)是否存在未對(duì)賬訂單 
  22.     checkOrders = checkOrders(); 

那么,如何解決這個(gè)問題呢?我們繼續(xù)往下看。

新的解決方案

相信細(xì)心的小伙伴們能夠看出,整個(gè)業(yè)務(wù)的場(chǎng)景就是:一個(gè)線程需要等待其他兩個(gè)線程的邏輯執(zhí)行完畢后再執(zhí)行。在Java的并發(fā)類庫(kù)中,為我們提供了一個(gè)能夠在這種場(chǎng)景下使用的類庫(kù),那就是CountDownLatch類。

使用CountDownLatch類優(yōu)化我們程序的具體做法就是:在程序的while()循環(huán)中首先創(chuàng)建一個(gè)CountDownLatch對(duì)象,計(jì)數(shù)器的值初始化為2。分別在hasNoOrders = getHasNoOrders();代碼和hasNoStock = getHasNoStock();代碼的后面調(diào)用latch.countDown()方法使得計(jì)數(shù)器的值分別減1。在主線程中調(diào)用latch.await()方法,等待計(jì)數(shù)器的值變?yōu)?,繼續(xù)往下執(zhí)行。這樣,就能夠完美解決我們遇到的問題了。優(yōu)化后的代碼如下所示。

  1. //檢測(cè)是否存在未對(duì)賬訂單 
  2. checkOrders = checkOrders(); 
  3. //創(chuàng)建線程池 
  4. Executor executor =  Executors.newFixedThreadPool(2); 
  5. while(checkOrders != null){ 
  6.     CountDownLatch latch = new CountDownLatch(2); 
  7.     executor.execute(()->{ 
  8.         //查詢未校對(duì)的訂單信息 
  9.         hasNoOrders = getHasNoOrders(); 
  10.         latch.countDown(); 
  11.     }); 
  12.     executor.execute(()->{ 
  13.        //查詢未校對(duì)的庫(kù)存記錄 
  14.        hasNoStock = getHasNoStock(); 
  15.        latch.countDown(); 
  16.     }); 
  17.      
  18.     //等待子線程的邏輯執(zhí)行完畢 
  19.     latch.await(); 
  20.   
  21.     //校對(duì)數(shù)據(jù)并返回結(jié)果 
  22.     checkResult = checkData(hasNoOrders, hasNoStock); 
  23.     //將結(jié)果信息保存到數(shù)據(jù)校對(duì)信息表中 
  24.     saveCheckResult(checkResult); 
  25.     //檢測(cè)是否存在未對(duì)賬訂單 
  26.     checkOrders = checkOrders(); 

至此,我們就完成了系統(tǒng)的優(yōu)化工作。

總結(jié)與思考

這次系統(tǒng)性能的優(yōu)化,主要是將單線程執(zhí)行的數(shù)據(jù)校對(duì)業(yè)務(wù),優(yōu)化成使用多線程執(zhí)行。在平時(shí)的工作過程中,我們需要認(rèn)真思考,找到系統(tǒng)性能瓶頸所在,找出在邏輯上不相干,并且沒有先后順序的業(yè)務(wù)邏輯,將其放到不同的線程中執(zhí)行,能夠大大提供系統(tǒng)的性能。

這次,對(duì)于系統(tǒng)的優(yōu)化,我們最終使用線程池來執(zhí)行比較耗時(shí)的查詢訂單與查詢庫(kù)存記錄的操作,并且在主線程中等待線程池中的線程邏輯執(zhí)行完畢后再執(zhí)行主線程的后續(xù)業(yè)務(wù)邏輯。這種場(chǎng)景,使用Java中提供的CountDownLatch類再合適不過了。這里,再?gòu)?qiáng)調(diào)一下:CountDownLatch主要的使用場(chǎng)景就是一個(gè)線程等待多個(gè)線程執(zhí)行完畢后再執(zhí)行。如下圖所示。

這里,也進(jìn)一步提醒了我們:如果想學(xué)好并發(fā)編程,熟練的掌握J(rèn)ava中提供的并發(fā)類庫(kù)是我們必須要做到的。

本文轉(zhuǎn)載自微信公眾號(hào)「冰河技術(shù)」,可以通過以下二維碼關(guān)注。轉(zhuǎn)載本文請(qǐng)聯(lián)系冰河技術(shù)公眾號(hào)。

 

責(zé)任編輯:武曉燕 來源: 冰河技術(shù)
相關(guān)推薦

2024-10-29 08:21:05

2021-02-02 15:38:19

Disruptor緩存Java

2022-04-21 07:51:51

場(chǎng)景JavaSQL

2013-11-13 15:28:31

Windows Ser

2020-01-06 15:41:52

AMD IntelCPU

2024-07-17 08:25:44

2021-07-05 14:55:28

前端優(yōu)化圖片

2019-03-15 15:00:49

Webpack構(gòu)建速度前端

2021-09-13 10:25:35

開發(fā)技能代碼

2015-09-16 15:21:23

Android性能優(yōu)化內(nèi)存

2024-09-19 08:09:37

MySQL索引數(shù)據(jù)庫(kù)

2015-09-16 15:48:55

Android性能優(yōu)化電量

2015-09-16 14:37:50

Android性能優(yōu)化運(yùn)算

2015-09-16 13:54:30

Android性能優(yōu)化渲染

2021-06-21 09:54:24

Windows 11CPU操作系統(tǒng)

2020-06-04 16:57:07

移動(dòng)開發(fā)互聯(lián)網(wǎng)實(shí)踐

2024-01-03 08:20:05

Java字符串性能

2022-09-09 09:33:14

支付寶代碼性能

2017-02-21 06:56:21

阿里云

2023-02-02 14:22:16

iOS 16.3蘋果續(xù)航
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)