解決吞吐性能問題時的思路


什么是Throughput

Throughput指的是應用處理任務的速率,它所描述的是應用在單位時間內能夠處理多大數量的任務

如下,如果應用能夠在1s中處理3個task,我們可以說它的throughput是3tps

1

值得注意的是,throughput這個指標所代表的是速率,它並不代表同時性(Concurrency),比如圖一中的3tps的應用,我們能說它可以在1s中處理3個task,但是並不意味着3個task是同時被處理的,而可能是順序、線性地被處理

如果應用可以支持同時處理多個任務,比如應用(系統)中有2個worker,每個worker都可以並行地在1s中內處理3個task,它的throughput則是6tps

2

如何提高throughput呢?顯然可以想到:

  1. 縮短每個任務處理的耗時
  2. 讓更多的任務可以被同時處理 - 增加並行能力

下圖中的應用(系統)可以支持同時處理3個任務,並且每個任務的處理耗時縮短到一半,其throughput是18tps

3

並行中的共享資源和鎖

如果對任務的處理需要訪問/修改共享資源呢?比如在扣減庫存的任務中,每個任務都需要去訪問(校驗)當前庫存余量,並且要修改(扣減)它

對共享資源的並發訪問和修改會產生沖突和一致性問題,比如有兩個扣減庫存的任務正在同時進行,此時庫存余量為1,兩個任務都從存儲中拿到了當前的庫存余量,當其中一個任務完成后,庫存余量被扣減為0,此時另一個任務已經完成了校驗過程,再去扣減庫存的時候,庫存余量就被更新成了-1

我們通常使用鎖來解決對共享資源的爭用所導致的並發沖突與一致性問題,使用資源鎖來隔離對資源的操作,保證數據的一致性(正確性)

4

如圖,在試圖使用某個資源之前,先獲取鎖從而占用住這個資源,隔離掉其它任務對此資源的訪問/修改,從而在占用時間里保證資源的一致性,再使用結束后則釋放鎖,使資源可以被其它任務訪問

為了避免並發沖突以及獲取一致性,並非一定要通過鎖,也會有其它的方法(比如原子操作),但是總體上還是在對資源的訪問/修改制造隔離

隔離的后果是什么呢?

鎖(共享資源)的爭用和等待

5

爭用會導致wait time

也因為我們對資源的訪問/修改進行了隔離,導致了多個任務的處理無法同時使用共享資源,每個任務都要等待其它任務對資源的占用結束后才可以繼續進行處理,這個等待就會產生wait time

這些wait time意味着:

  1. 任務的處理時間被延長 - 體現為latency
  2. 並行的任務越多,wait time越長 - 破壞了並行處理任務的能力

降低鎖的成本

降低使用鎖的費用

鎖的創建、獲取、釋放和銷毀都是有代價的,降低使用鎖本身的費用,比如把數據庫鎖換成redis鎖,甚至換成本地內存鎖

以減庫存為例: 提前把庫存放到redis里,從redis中扣減

降低鎖的占用時間

在獲取到資源鎖之后,應該盡快地釋放它,盡量不要在占用鎖的期間里做比較花費時間的事情,比如:1)發送HTTP請求 2)執行昂貴的SQL語句 3)超時等待 等等

但是它有局限,如果我們嘗試增加任務的並行數,wait time就會繼續隨之增長

6
6-1

使用更細粒度的鎖(共享資源)

通過盡量使用更細粒度的鎖(共享資源),可以使鎖爭用(碰撞)的概率更低,出現等待的情況也就更少

7

以減庫存為例: 把庫存分布到多個籃子里,100=10*10,隨機或者按策略去某個籃子里扣減,這樣原本是所有任務都使用單一的庫存余量,現在變成分散地使用10個庫存余量,出現等待的概率就會變少

無論是降低鎖成本還是降低鎖粒度,其目的都是減少爭用的發生,減少任務的wait time,從而可以提高對多任務的並行處理能力

緩沖請求,合並任務,批量處理(Buffer-Merge-Process)

8

以減庫存為例:

  1. 把減庫存請求丟進隊列中
  2. 每次從隊列中取出多個減庫存請求,合並成一個減庫存任務
  3. 處理合並后的減庫存任務

這種方式會導致單個任務的耗時增加 - 因為任務不會立即被處理,但是可以增加總體的throughput,某種程度上是用延遲交換了吞吐,需要考慮這個交換是否值得

總體思路

1.首先定位爭用

2.減少爭用,減少Wait Time

3.最后才嘗試Buffer-Merge-Process

通常這三個方法都可以嘗試,都有着一些成本和副作用,也經常需要結合使用,但是在考慮解決方案時,優先考慮解決鎖爭用

共享資源爭用是個很糟糕的質量信號,即使在當前看起來它沒有產生很嚴重的后果,但是實際上它有着非常大的隱患

1.它會導致應用在性能上變得脆弱:我們可以通過減少鎖的費用和占用時間來減少爭用從而提高性能,相反的,當鎖的費用上升以及占用時間增加時,很容易大量爭用導致性能急劇下降,而這時想要解決性能問題很可能要付出非常大的成本 。比如網絡環境變化導致使用鎖時的延遲增高,又或者一個業務需求或者bugfix需要你在占用鎖時執行一個昂貴的sql語句或者http請求,甚至可能只是一個輕微的網絡波動,都可能導致應用的吞吐劇烈下降

資源爭用下的Scalability問題

當我們在開發應用的時候,對於應用的吞吐性能可以有三種要求:

  1. 夠用就行,只要能滿足當前需求即可
  2. 吞吐性能不僅要夠用,還要出色,比如當前業務只需要我們的應用有30tps,但是我們在設計和開發時,要以1000tps的性能質量來要求它
  3. 當前的吞吐性能需要滿足當前的業務需求,不要求應用具備過高的吞吐性能,但是要求在將來它可以通過較低的成本來提升到更高的吞吐性能

第三種要求實際上就是對於應用的scalability的要求,它不要求過高的吞吐性能,但是它需要應用能夠快速地響應業務需求對於吞吐性能要求的提升

即當外部環境變化時 - 比如業務規模的增長,比如一個重要的feature帶來了性能的降低,比如雲遷移導致了應用運行環境發生了變化,這時我們需要能夠通過調配資源能夠簡單快速的提升應用的吞吐性能來適應新的需求,先快速地做到"Doing more",然后再去"With less"

福祿·架構組 龍舌蘭日落


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM