欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

關(guān)于IO密集型服務(wù)提升性能的三種方式

 更新時(shí)間:2024年07月04日 09:19:06   作者:xindoo  
這篇文章主要介紹了關(guān)于IO密集型服務(wù)提升性能的三種方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教

IO密集型服務(wù)提升性能的三種方式

大部分的業(yè)務(wù)系統(tǒng)其實(shí)都是IO密集型的系統(tǒng),比如像我們面向B端提供攝像頭服務(wù),很多的接口其實(shí)就是將各種各樣的數(shù)據(jù)匯總起來(lái),展示給用戶,我們的數(shù)據(jù)來(lái)源包括Redis、Mysql、Hbase、以及依賴的一些服務(wù)方的數(shù)據(jù),并不涉及到太多復(fù)雜的計(jì)算邏輯。

在過(guò)去的半年中,因?yàn)槲覀償?shù)據(jù)量和業(yè)務(wù)復(fù)雜性的增長(zhǎng),確實(shí)遇到了一些明顯的性能問(wèn)題,分析大部分問(wèn)題的本質(zhì)原因就是IO太慢了。

我們系統(tǒng)中最復(fù)雜的計(jì)算邏輯執(zhí)行最慢也就微秒級(jí),而調(diào)一次數(shù)據(jù)庫(kù)最快也得1-2毫秒,有著2-3個(gè)數(shù)量級(jí)的差距。

然而IO又是業(yè)務(wù)系統(tǒng)中不可能干掉的操作,但頻繁或者錯(cuò)誤的使用IO會(huì)給系統(tǒng)帶來(lái)非常明顯的性能問(wèn)題,輕則拖慢接口影響用戶體驗(yàn),重則OOM直接宕機(jī)。

針對(duì)IO問(wèn)題帶來(lái)性能問(wèn)題,這里我總結(jié)了三種方式 批處理、緩存和多線程,雖然看起來(lái)是很簡(jiǎn)單的操作,但還是得在合適的地方正確使用才能發(fā)揮出這三種方法的價(jià)值。

批處理

首先是批處理,這里先說(shuō)一個(gè)真實(shí)的案例, 在2021年我們?cè)谧龇?wù)上云過(guò)程中,有個(gè)接口上云后,時(shí)延從原本的50ms左右漲到了150ms,后來(lái)排查發(fā)現(xiàn),之前是串行化去調(diào)用KMS,這個(gè)服務(wù)上云后和KMS的服務(wù)端出現(xiàn)了跨機(jī)房調(diào)用,單次KMS的調(diào)用時(shí)長(zhǎng)增長(zhǎng)了近0.5ms。 單看這0.5ms確實(shí)不算多,但也架不住幾十次的串行調(diào)用累計(jì)到一起,最終出現(xiàn)了100ms的總延時(shí)增長(zhǎng)。這種接口時(shí)延增長(zhǎng)大到原來(lái)的三倍,用戶是很容易感受到的,可能他們的感受就是這應(yīng)用真卡!

上面這個(gè)問(wèn)題復(fù)現(xiàn)起來(lái)很簡(jiǎn)單,其實(shí)就一個(gè)for循環(huán),串行去調(diào)用kms解密數(shù)據(jù)量。

for (String str : strList) {
   decodedStr = kmsClient.decrypt(str);  // 單次調(diào)用需要0.5-1ms,串行100次需要50-100ms
}

上述代碼整體的主要的耗時(shí)其實(shí)并不是kms對(duì)數(shù)據(jù)解密的過(guò)程上(僅需要微秒級(jí)),而是請(qǐng)求發(fā)送和接收結(jié)果數(shù)據(jù)時(shí)數(shù)據(jù)在網(wǎng)絡(luò)上傳輸?shù)暮臅r(shí),這就取決于雙方服務(wù)之間的物理距離了,我們大部分服務(wù)都是在北京部署,但仍會(huì)出現(xiàn)跨機(jī)房調(diào)用的情況,這個(gè)時(shí)候網(wǎng)絡(luò)延時(shí)也會(huì)增長(zhǎng)0.5-1ms。批處理提升IO性能的原理,其實(shí)就是用單次網(wǎng)絡(luò)IO替代掉原有的多次網(wǎng)絡(luò)IO,IO時(shí)長(zhǎng)越長(zhǎng),優(yōu)化效果越顯著。 用一個(gè)生活中的例子大家更容易理解些,假設(shè)你要給家里準(zhǔn)備一份晚餐,其中很重要的一步就是去菜市場(chǎng)買菜,你是一樣一樣買?還是一次性全買齊了? 這就是單次處理和批處理的區(qū)別。

這個(gè)性能問(wèn)題看似簡(jiǎn)單,其實(shí)在實(shí)際編程過(guò)程中經(jīng)常犯,稍不留神就大批量串行IO調(diào)用,比如在for循環(huán)中查庫(kù)(你是不是已經(jīng)在腦海中想到自己寫的問(wèn)題代碼了)。 如何避免自己在日常編程中出現(xiàn)類似的問(wèn)題,我總結(jié)了一條編程指導(dǎo)經(jīng)驗(yàn),那就是 在任何循環(huán)中盡量不要產(chǎn)生IO調(diào)用,除非你知道自己在做什么。

當(dāng)然也不是所有的IO都會(huì)產(chǎn)生問(wèn)題,有些IO非常快,而且你串行的頻次也不是很高,貿(mào)然將代碼改成批處理的邏輯會(huì)顯著增加代碼復(fù)雜度,增加維護(hù)成本反而得不償失,所以建議還是根據(jù)具體的IO類型和具體需求,評(píng)估具體是否要做批處理。

以下我給出一些具體的IO類型和單次IO耗時(shí)參考值,大家寫代碼的時(shí)候可以關(guān)注下。

IO類型耗時(shí)備注
SSD固態(tài)磁盤隨機(jī)訪問(wèn)0.1ms目前大部分服務(wù)器在使用SSD了,小文件讀寫的耗時(shí)幾乎可以不關(guān)注,但如果文件非常大時(shí),這里各方的帶寬就是瓶頸,耗時(shí)也容易快速增長(zhǎng),重點(diǎn)關(guān)注大文件。
Redis訪問(wèn)0.1ms簡(jiǎn)單Redis查詢,主要還是在網(wǎng)絡(luò)上,Redis服務(wù)自身處理請(qǐng)求僅幾十us,只要不出大key,基本沒(méi)問(wèn)題。
mysql查詢1-10ms簡(jiǎn)單查詢可以在10ms下,但涉及到復(fù)雜查詢或者大量數(shù)據(jù)無(wú)索引的情況下,耗時(shí)會(huì)顯著增長(zhǎng)。mysql的異常查詢是很多業(yè)務(wù)系統(tǒng)的性能問(wèn)題主要來(lái)源。
HDD機(jī)械磁盤隨機(jī)訪問(wèn)10ms主要磁盤尋道時(shí)間,取決于磁盤轉(zhuǎn)速,如果你恰好用了HDD又想讀寫文件,無(wú)論文件大小這部分耗時(shí)是一定不能忽略的。
調(diào)用第三方服務(wù)1-100ms取決于依賴方的接口性能,不同接口延時(shí)的方差非常大,調(diào)用第三方接口,性能和容量都需要非常仔細(xì)的評(píng)估。
同城跨機(jī)房RTT0.5ms-
物理距離每增加50-100公里rtt +1ms延時(shí)主要來(lái)源于光在光纖中的傳播耗時(shí)+交換機(jī)和路由器的處理耗時(shí),比如從廣州到北京,一個(gè)RTT就需要50ms,對(duì)接外部服務(wù)接口,如果關(guān)注性能,物理距離一定要考慮進(jìn)去。

緩存

高IO的應(yīng)用有個(gè)特點(diǎn),就是大量的數(shù)據(jù)其實(shí)是被重復(fù)加載的,這也是”局部性“的一個(gè)體現(xiàn),局部性告訴我們,只有少量的數(shù)據(jù)會(huì)被大量的加載。

利用局部性,我們只要將重要的小部分?jǐn)?shù)據(jù)緩存起來(lái),就可以減少大量的IO,從而提升我們系統(tǒng)的性能。

如果我們用平均延時(shí)來(lái)評(píng)估性能,我們可以用一個(gè)平均延遲計(jì)算公式來(lái)描述加緩存后的性能:

avgLatency = hitRate * cacheLatency +  (1 - hitRate) * originalLatency

其中avgLatency代指加了緩存后的平均延遲,hitRate表示緩存的命中率,cacheLatency指的是訪問(wèn)一次緩存所需要的耗時(shí),在實(shí)際使用中,如果我們使用了本地緩存,我們可以簡(jiǎn)單粗暴認(rèn)為cacheLatency是0,以上公式就可以簡(jiǎn)化為avgLatency = (1 - hitRate) * originalLatency 。

從簡(jiǎn)化后的公式可以看出加緩存后的效果僅跟緩存的命中率有關(guān)系,如果cache命中率是90%,就會(huì)有10倍的性能提升,如果是99%就會(huì)有100百性能提升(簡(jiǎn)略計(jì)算),只要我們無(wú)限提升緩存命中率,似乎就能無(wú)限提升性能。

那命中率又和什么相關(guān)呢?

答案就是數(shù)據(jù)的分布、緩存的大小和數(shù)據(jù)的淘汰策略三者相關(guān)。

  • 數(shù)據(jù)分布: 現(xiàn)實(shí)世界中,大部分?jǐn)?shù)據(jù)的訪問(wèn)都受局部性的影響,用大白話講就是只有少部分?jǐn)?shù)據(jù)會(huì)被頻繁訪問(wèn),如果把數(shù)據(jù)被訪問(wèn)頻次曲線畫出來(lái),如上圖。
  • 緩存大?。?/strong> 這個(gè)很好理解,只要緩存的數(shù)據(jù)足夠多,緩存命中率就越高。
  • 淘汰策略: 淘汰策略是指在緩存容量不足的情況下,如何剔除價(jià)值最低的數(shù)據(jù),常見的淘汰策略有LRU、LFU、FIFO,我們實(shí)際情況中用的最多的就是LRU。

正確考慮到以上三點(diǎn)后,我們大部分情況下是可以將少量高頻被訪問(wèn)的數(shù)據(jù)緩存起來(lái),從而提升系統(tǒng)性能。

使用Cache有個(gè)額外需要注意的一項(xiàng)就是數(shù)據(jù)一致性,在cache的使用過(guò)程中緩存命中率和數(shù)據(jù)一致性幾乎就是相悖的,很難做到兩全其美,就比如我之前有篇文章說(shuō)過(guò)CPU Cache,其實(shí)就是硬件層面使用Cache優(yōu)化IO性能的一個(gè)典型案例,但CPU為保證數(shù)據(jù)一致性卻給當(dāng)代程序員留下一堆"坑"。

在實(shí)際工作中,關(guān)于Cache實(shí)現(xiàn)我們有很多選擇,常用的比如Guava中的LoadingCache、caffiene、ehcache、redis,spring中也有spring-cache 高級(jí)封裝,這些如果你都不想用的話,你都可以用Map自己擼一個(gè)……

多線程

以上兩種方式的本質(zhì),其實(shí)是通過(guò)優(yōu)化非必要的IO次數(shù)來(lái)提升性能,但現(xiàn)實(shí)情況中并不是所有的IO都可以被優(yōu)化掉,針對(duì)這種情況,其實(shí)也就只多線程一條路可選了。

這個(gè)思路也很好理解,用大白話來(lái)說(shuō),如果活太多干不完就多招兩個(gè)人來(lái)干。 在IO密集型系統(tǒng)中,多線程的優(yōu)勢(shì)在于它能充分利用CPU的計(jì)算能力。

當(dāng)一個(gè)線程在等待IO操作(如網(wǎng)絡(luò)請(qǐng)求或磁盤讀寫)完成時(shí),CPU可以切換到其他線程去執(zhí)行其他任務(wù),而不是閑置不用。

這樣,我們就可以充分利用CPU資源,提高系統(tǒng)的響應(yīng)速度。

但是,使用多線程并非沒(méi)有代價(jià)。首先,需要注意的是線程切換的開銷。如果線程數(shù)量過(guò)多,線程切換的開銷可能會(huì)消耗大量的CPU資源。其次,使用多線程會(huì)顯著增加代碼的復(fù)雜度,需要考慮到很多并發(fā)相關(guān)的問(wèn)題,如:線程間的同步、死鎖、資源競(jìng)爭(zhēng)等,這些都需要在編程時(shí)仔細(xì)考慮和處理,稍有不慎就會(huì)引入很難排查的Bug。

在Java中,我們可以通過(guò)使用ExecutorService、CompletableFuture等工具來(lái)創(chuàng)建并管理線程。當(dāng)然,我們也可以直接使用Thread類來(lái)創(chuàng)建線程,但線程需要自行管理,不是很推薦。同時(shí),Java提供了許多同步和并發(fā)工具,如synchronized關(guān)鍵字、ReentrantLock、Semaphore等,以幫助我們處理并發(fā)問(wèn)題。

在多線程優(yōu)化中,線程池的使用是非常常見的。線程池可以有效地管理和復(fù)用線程,避免了頻繁地創(chuàng)建和銷毀線程所帶來(lái)的開銷。在Java中,我們可以使用ExecutorService來(lái)創(chuàng)建一個(gè)線程池,然后將任務(wù)提交給線程池來(lái)執(zhí)行。在Java8及以上的版本中,我們也可用使用parallelStream()很方便的將代碼改造成多線程,但需注意parallelStream底層是使用同一個(gè)ForkJoinPool,大量使用可能會(huì)出現(xiàn)相互干擾的情況。

另一個(gè)常見的多線程優(yōu)化方式是使用異步編程。異步編程可以讓程序在等待IO操作完成的時(shí)候,不必阻塞當(dāng)前線程,而是可以切換到其他任務(wù)進(jìn)行處理。

在Java中,我們可以使用Future、CompletableFuture等工具來(lái)進(jìn)行異步編程。

總的來(lái)說(shuō),多線程可以是一個(gè)強(qiáng)大的工具,可以顯著提高IO密集型系統(tǒng)的性能。但是,使用多線程也需要謹(jǐn)慎,需要處理好并發(fā)問(wèn)題,才能確保程序的正確性和穩(wěn)定性。

總結(jié)

在面對(duì)IO密集型系統(tǒng)性能優(yōu)化時(shí),我們可以通過(guò)三種主要的方式來(lái)進(jìn)行:批處理、緩存和多線程。這三種方式各有其優(yōu)點(diǎn)和適用場(chǎng)景。

  • 批處理可以通過(guò)減少網(wǎng)絡(luò)IO次數(shù),顯著減少網(wǎng)絡(luò)傳輸?shù)难舆t時(shí)間,從而提升系統(tǒng)性能。但是,它需要我們仔細(xì)分析和設(shè)計(jì)我們的數(shù)據(jù)處理流程,才能找到合適的批處理策略。
  • 緩存則是通過(guò)存儲(chǔ)頻繁訪問(wèn)的數(shù)據(jù),減少了對(duì)慢速存儲(chǔ)(如磁盤或網(wǎng)絡(luò))的訪問(wèn),從而提升性能。但是,使用緩存時(shí)需要考慮數(shù)據(jù)的一致性問(wèn)題,以及如何選擇合適的緩存淘汰策略。
  • 多線程則是通過(guò)并行處理多個(gè)任務(wù),充分利用CPU的計(jì)算能力,從而提升性能。但是,使用多線程需要處理并發(fā)問(wèn)題,以及線程管理和調(diào)度的開銷。

在實(shí)際應(yīng)用中,這三種方式往往會(huì)結(jié)合使用,以適應(yīng)不同的性能需求和系統(tǒng)環(huán)境。

選擇哪種方式,或者如何結(jié)合使用,需要根據(jù)具體的業(yè)務(wù)需求、系統(tǒng)環(huán)境和性能目標(biāo)來(lái)決定。

在進(jìn)行性能優(yōu)化時(shí),我們需要深入理解我們的系統(tǒng),找出性能瓶頸,然后有針對(duì)性的進(jìn)行優(yōu)化。

同時(shí),我們還需要通過(guò)性能測(cè)試和監(jiān)控,來(lái)驗(yàn)證我們的優(yōu)化效果,以及及時(shí)發(fā)現(xiàn)和解決新的性能問(wèn)題。

只有通過(guò)這樣的方式,我們的系統(tǒng)才能持續(xù)提供高效、穩(wěn)定的服務(wù)。

以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • Spring Boot整合消息隊(duì)列RabbitMQ的實(shí)現(xiàn)示例

    Spring Boot整合消息隊(duì)列RabbitMQ的實(shí)現(xiàn)示例

    本文主要介紹了Spring Boot整合消息隊(duì)列RabbitMQ的實(shí)現(xiàn)示例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2025-03-03
  • Java實(shí)現(xiàn)配置加載機(jī)制

    Java實(shí)現(xiàn)配置加載機(jī)制

    這篇文章主要介紹了Java實(shí)現(xiàn)配置加載機(jī)制的相關(guān)資料,需要的朋友可以參考下
    2016-01-01
  • mybatis 如何判斷l(xiāng)ist集合是否包含指定數(shù)據(jù)

    mybatis 如何判斷l(xiāng)ist集合是否包含指定數(shù)據(jù)

    這篇文章主要介紹了mybatis 判斷l(xiāng)ist集合是否包含指定數(shù)據(jù)的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-06-06
  • Java異常處理實(shí)例詳解

    Java異常處理實(shí)例詳解

    這篇文章主要介紹了Java異常處理實(shí)例詳解,列舉了實(shí)際例子講解的很清晰,有感興趣的同學(xué)可以學(xué)習(xí)下
    2021-03-03
  • 簡(jiǎn)單了解JAVA內(nèi)存泄漏和溢出區(qū)別及聯(lián)系

    簡(jiǎn)單了解JAVA內(nèi)存泄漏和溢出區(qū)別及聯(lián)系

    這篇文章主要介紹了簡(jiǎn)單了解JAVA內(nèi)存泄漏和溢出區(qū)別及聯(lián)系,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-03-03
  • Java技術(shù)長(zhǎng)久占居主要地位的12個(gè)原因

    Java技術(shù)長(zhǎng)久占居主要地位的12個(gè)原因

    這篇文章主要為大家詳細(xì)介紹了12個(gè)Java長(zhǎng)久占居主要地位的原因,感興趣的小伙伴們可以參考一下
    2016-07-07
  • 深入學(xué)習(xí)java ThreadLocal的源碼知識(shí)

    深入學(xué)習(xí)java ThreadLocal的源碼知識(shí)

    ThreadLocal是一個(gè)本地線程副本變量工具類。主要用于將私有線程和該線程存放的副本對(duì)象做一個(gè)映射,各個(gè)線程之間的變量互不干擾,特別適用于各個(gè)線程依賴不通的變量值完成操作的場(chǎng)景。下面我們來(lái)詳細(xì)了解一下它吧
    2019-06-06
  • MyBatis傳入List集合查詢數(shù)據(jù)問(wèn)題

    MyBatis傳入List集合查詢數(shù)據(jù)問(wèn)題

    這篇文章主要介紹了MyBatis傳入List集合查詢數(shù)據(jù)問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-02-02
  • 總結(jié)Java調(diào)用Python程序方法

    總結(jié)Java調(diào)用Python程序方法

    這篇文章主要介紹了總結(jié)Java調(diào)用Python程序方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-08-08
  • Java 處理超大數(shù)類型之BigInteger案例詳解

    Java 處理超大數(shù)類型之BigInteger案例詳解

    這篇文章主要介紹了Java 處理超大數(shù)類型之BigInteger案例詳解,本篇文章通過(guò)簡(jiǎn)要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下
    2021-09-09

最新評(píng)論