面試官:做過支付資產?那先聊聊熱點賬戶吧
背景
當前形勢不佳,在這種情況下。小貓更是雪上加霜,他被裁了。投了個把月簡歷,終于約到一個面試。
面試官:“看你簡歷上寫了支付和賬戶相關項目,那能否聊一下熱點賬戶問題你們是咋處理的吧”。
小貓懵逼了一會,“額?什么是熱點賬戶?我們好像模型里面就一個資產賬戶,然后充值的時候和消費的時候更新一下該賬戶,并且記錄一下操作明細,然后結束了。”
面試官:“哦。回去等通知吧。”
出來之后,小貓整個人都還是懵逼的。
問題分析
我們一起來看一下這樣一個問題,其實這里面試官想要知道的是,在高并發的情況下,針對熱點賬戶如何進行賬戶金額的沖扣,小貓沒有get到面試官的點,可能他負責的項目中本身的量不大,壓根就沒有想過這類問題。如果問的是你,你該如何應對呢?
接下來,咱們一起從以下幾點來剖析一下這個問題吧。
目錄
什么是熱點賬戶?
熱點賬戶一般指被高頻更新的賬戶,比如短時間內大量的賬戶余額更新請求集中在極少數賬戶上。這類賬戶雖然數量不多,但更新頻率很高,處理不當可能會帶來嚴重的性能問題,影響其他賬戶的正常讀寫操作。
我們來看一下熱點商家賬戶S的例子,可能更容易讓人理解。大量請求并發打到我們數據庫底層表的時候,底層賬戶S究竟發生了什么。假設我們現在有用戶A、用戶B、用戶C,等等可能更多,另外有一個商家賬戶S,我們暫時枚舉三個,那么當其同時并發打到底層數據庫的時候,就有如下圖所示。
數據庫底層更新
上圖中我們可以看到,對于同一個商家賬戶S,由于實際的業務需要更新可用賬戶余額,所以單筆沖扣都是在一個事務中進行的,任何的更新行為都會對數據庫上行鎖。由于并發量大,請求的數量又多,大家很容易就能想到鎖的等待問題會嚴重影響性能。
熱點賬戶的分類
上述我們知道了什么是熱點賬戶,并且知道了熱點賬戶產生的原因。其實熱點賬戶根據資金的出入可以分成三種。
- 雙頻賬戶 :上圖中我們看到商家賬戶既有出也有入,那其實這樣的賬戶就可以理解為雙頻賬戶。雙頻賬戶指入賬頻次以及出賬頻次都很高的賬戶。
- 加頻賬戶:大家可能可快就知道了還有純入賬頻次高的,那么這個就是加頻賬戶。
- 減頻賬戶:純出賬的賬戶那么即為減頻賬戶。
針對這樣的三種賬戶類型,大家其實可以聯想一下這些賬戶對應哪些生活中的場景。
熱點賬戶解決方案
透過現象看本質,其實要解決熱點賬戶問題,其實就是解決數據庫壓力過大,數據庫表更新失敗,執行效率過低的問題。那么解決該類問題,閣下該如何應對?(其實小貓如何可以理解面試官的用意,解決方案應該可以想到幾個)
1.提升硬件設備性能
如果數據庫壓力過大,數據庫執行效率低,最簡單的方式就是調整硬件設備唄。把連接池優化,把CPU優化,把磁盤優化,內存優化等等。在此不展開贅述。老貓給他定義了個別名“大力出奇跡法”。
2.限流法
這種其實也很簡單,但是有點粗暴,數據庫壓力大,那么咱們就讓流量少一點打到數據庫層唄。做個限流不就結了么。
限流
這種方式無論是上述那種類型的熱點賬戶,顯然都支持這種優化方法。
但是用戶能滿意么?買個東西老半天,重試好久都是支付失敗,用戶估計會跳起來吧。
這種犧牲用戶體驗的方案不是一點用處都沒有,這種其實完全可以配合其他方案一起,作為一種最終的兜底方案。
3.預寫記賬日志(WAL-Write Ahead Log)
很多中間件類似于zk、etcd、es等,包括mysql底層以及很多的操作系統其實都用到了WAL的思想,感興趣的小伙伴可以找一下相關資料。我們也借鑒這種思想,mysql在執行insert語句的時候的效率,其實要比Update執行效率高得多,更新的時候需要獲取讀和寫,但是insert只需要執行順序插入即可。因此咱們就有了下面了這樣的設想方案。
我們先將賬務明細插入到MySQL中,再讀取明細,完成賬戶底層的更新動作。
- 優點:實時的交易全部是insert賬務明細,能大大提高入賬速度,賬戶最終更新的頻度我們可以自行把控,減少并發帶來的壓力。
- 缺點:賬戶更新存在延遲,這樣的話有可能會造成賬戶透支的風險。
- 適用:所以這種方案加頻類型的熱點賬戶非常適用,但是對于減頻賬戶以及雙頻賬戶就需要結合具體業務慎重考慮,因為會存在賬戶透支可能。
4.異步削峰緩沖記賬
我們預寫記賬方式其實通過異步把控頻率進行更新賬戶,異步削峰模式其實和上述模式有點類似,說到削峰填谷大家很容易就能想到消息隊列,于是就有了我們下面的這種方案。
采用消息隊列的方式,可以緩解突然到來的大流量。消息多的時候會堆積在隊列中,然后被消費慢慢更新下去。
- 優點:避免了突增的流量給系統帶來的沖擊。
- 缺點:賬戶更新并不是及時的,另外的話,如果程序處理不當或者其他原因,會造成消息丟失,從而造成記賬錯誤,同樣也存在賬戶透支風險。
- 適用:對于加頻類型的賬戶比較適用,對于減頻賬戶以及雙頻賬戶慎用,同樣也會存在賬戶透支風險。譬如加頻場景:在B端收單賬戶與業務中間賬戶處理;減頻場景在C端微信、頭條等春節搶紅包入賬處理(注意賬戶透支風險)。
5.匯總明細記賬
關于該方案其實思路是這樣的,既然多次頻繁更新賬戶余額成為瓶頸,那么我們就將多次更新統計之后轉換為一次更新。如下圖:
匯總明細記賬
這種基于統計之后更新賬戶余額的行為,也是異步進行的。所以優缺點當然也是顯而易見的。
- 優點:化多次賬戶余額更新操作為一次,減少了數據庫的讀寫操作。
- 缺點:由于是異步進行,交易其實并不能實時入賬,如果是減頻賬戶場景的話,也還是會有賬戶透支風險。
- 適用:非常適用加頻熱點賬戶,對于減頻賬戶以及雙頻賬戶慎用。
6.賬戶拆分記賬
既然單個賬戶寫入的時候壓力過大,那么我們就將單個熱點賬戶拆分成多個子賬戶去分散每個賬戶的讀寫壓力。
賬戶拆分記賬
這種方案只要能夠處理好扣款時,子賬戶余額不夠扣,資金歸集處理得好,那么問題其實也能夠得到很好的解決。
- 優點:分散了單個熱點賬戶的寫入量。
- 缺點:子賬戶扣款的時候余額扣款處理需要做資金歸集,可能出現扣款失敗,但是如果歸集做的好,那么其實問題也不大。
- 適用:這種方案適用于所有類型的熱點賬戶類型。
7.緩存記賬
Mysql的讀寫能力不好,那么我們就去尋找比Mysql讀寫效率更高的中間件,將結果預先計算好,那么我們很容易就想到了用非關系型數據庫作為前置賬戶,比如redis,讓計算結果先在redis中計算完成,然后最終異步flush到mysql中。
redis緩存
- 優點:緩存的吞吐量遠遠高于mysql,而且速度很快。
- 缺點:數據容易丟失。
- 適用:這種方案適用于熱點賬戶的任意一種類型,但是由于其數據準確性的考慮,往往對金額不是那么敏感的可以采用該方法,例如刷短視頻得積分這種場景。可以采用該類方案。