什麼是Throughput
Throughput指的是應用處理任務的速率,它所描述的是應用在單位時間內能夠處理多大數量的任務
如下,如果應用能夠在1s中處理3個task,我們可以說它的throughput是3tps
值得注意的是,throughput這個指標所代表的是速率,它並不代表同時性(Concurrency),比如圖一中的3tps的應用,我們能說它可以在1s中處理3個task,但是並不意味著3個task是同時被處理的,而可能是順序、線性地被處理
如果應用可以支援同時處理多個任務,比如應用(系統)中有2個worker,每個worker都可以並行地在1s中內處理3個task,它的throughput則是6tps
如何提高throughput呢?顯然可以想到:
- 縮短每個任務處理的耗時
- 讓更多的任務可以被同時處理 - 增加並行能力
下圖中的應用(系統)可以支援同時處理3個任務,並且每個任務的處理耗時縮短到一半,其throughput是18tps
並行中的共享資源和鎖
如果對任務的處理需要訪問/修改共享資源呢?比如在扣減庫存的任務中,每個任務都需要去訪問(校驗)當前庫存餘量,並且要修改(扣減)它
對共享資源的併發訪問和修改會產生衝突和一致性問題,比如有兩個扣減庫存的任務正在同時進行,此時庫存餘量為1,兩個任務都從儲存中拿到了當前的庫存餘量,當其中一個任務完成後,庫存餘量被扣減為0,此時另一個任務已經完成了校驗過程,再去扣減庫存的時候,庫存餘量就被更新成了-1
我們通常使用鎖來解決對共享資源的爭用所導致的併發衝突與一致性問題,使用資源鎖來隔離對資源的操作,保證資料的一致性(正確性)
如圖,在試圖使用某個資源之前,先獲取鎖從而佔用住這個資源,隔離掉其它任務對此資源的訪問/修改,從而在佔用時間裡保證資源的一致性,再使用結束後則釋放鎖,使資源可以被其它任務訪問
為了避免併發衝突以及獲取一致性,並非一定要通過鎖,也會有其它的方法(比如原子操作),但是總體上還是在對資源的訪問/修改製造隔離
隔離的後果是什麼呢?
鎖(共享資源)的爭用和等待
爭用會導致wait time
也因為我們對資源的訪問/修改進行了隔離,導致了多個任務的處理無法同時使用共享資源,每個任務都要等待其它任務對資源的佔用結束後才可以繼續進行處理,這個等待就會產生wait time
這些wait time意味著:
- 任務的處理時間被延長 - 體現為latency
- 並行的任務越多,wait time越長 - 破壞了並行處理任務的能力
降低鎖的成本
降低使用鎖的費用
鎖的建立、獲取、釋放和銷燬都是有代價的,降低使用鎖本身的費用,比如把資料庫鎖換成redis鎖,甚至換成本地記憶體鎖
以減庫存為例: 提前把庫存放到redis裡,從redis中扣減
降低鎖的佔用時間
在獲取到資源鎖之後,應該儘快地釋放它,儘量不要在佔用鎖的期間裡做比較花費時間的事情,比如:1)傳送HTTP請求 2)執行昂貴的SQL語句 3)超時等待 等等
但是它有侷限,如果我們嘗試增加任務的並行數,wait time就會繼續隨之增長
使用更細粒度的鎖(共享資源)
通過儘量使用更細粒度的鎖(共享資源),可以使鎖爭用(碰撞)的概率更低,出現等待的情況也就更少
以減庫存為例: 把庫存分佈到多個籃子裡,100=10*10,隨機或者按策略去某個籃子里扣減,這樣原本是所有任務都使用單一的庫存餘量,現在變成分散地使用10個庫存餘量,出現等待的概率就會變少
無論是降低鎖成本還是降低鎖粒度,其目的都是減少爭用的發生,減少任務的wait time,從而可以提高對多工的並行處理能力
緩衝請求,合併任務,批量處理(Buffer-Merge-Process)
以減庫存為例:
- 把減庫存請求丟進佇列中
- 每次從佇列中取出多個減庫存請求,合併成一個減庫存任務
- 處理合並後的減庫存任務
這種方式會導致單個任務的耗時增加 - 因為任務不會立即被處理,但是可以增加總體的throughput,某種程度上是用延遲交換了吞吐,需要考慮這個交換是否值得
總體思路
1.首先定位爭用
2.減少爭用,減少Wait Time
3.最後才嘗試Buffer-Merge-Process
通常這三個方法都可以嘗試,都有著一些成本和副作用,也經常需要結合使用,但是在考慮解決方案時,優先考慮解決鎖爭用
共享資源爭用是個很糟糕的質量訊號,即使在當前看起來它沒有產生很嚴重的後果,但是實際上它有著非常大的隱患
1.它會導致應用在效能上變得脆弱:我們可以通過減少鎖的費用和佔用時間來減少爭用從而提高效能,相反的,當鎖的費用上升以及佔用時間增加時,很容易大量爭用導致效能急劇下降,而這時想要解決效能問題很可能要付出非常大的成本 。比如網路環境變化導致使用鎖時的延遲增高,又或者一個業務需求或者bugfix需要你在佔用鎖時執行一個昂貴的sql語句或者http請求,甚至可能只是一個輕微的網路波動,都可能導致應用的吞吐劇烈下降
資源爭用下的Scalability問題
當我們在開發應用的時候,對於應用的吞吐效能可以有三種要求:
- 夠用就行,只要能滿足當前需求即可
- 吞吐效能不僅要夠用,還要出色,比如當前業務只需要我們的應用有30tps,但是我們在設計和開發時,要以1000tps的效能質量來要求它
- 當前的吞吐效能需要滿足當前的業務需求,不要求應用具備過高的吞吐效能,但是要求在將來它可以通過較低的成本來提升到更高的吞吐效能
第三種要求實際上就是對於應用的scalability的要求,它不要求過高的吞吐效能,但是它需要應用能夠快速地響應業務需求對於吞吐效能要求的提升
即當外部環境變化時 - 比如業務規模的增長,比如一個重要的feature帶來了效能的降低,比如雲遷移導致了應用執行環境發生了變化,這時我們需要能夠通過調配資源能夠簡單快速的提升應用的吞吐效能來適應新的需求,先快速地做到"Doing more",然後再去"With less"