面試官:做過(guò)支付資產(chǎn)?那先聊聊熱點(diǎn)賬戶吧
背景
當(dāng)前形勢(shì)不佳,在這種情況下。小貓更是雪上加霜,他被裁了。投了個(gè)把月簡(jiǎn)歷,終于約到一個(gè)面試。
面試官:“看你簡(jiǎn)歷上寫了支付和賬戶相關(guān)項(xiàng)目,那能否聊一下熱點(diǎn)賬戶問(wèn)題你們是咋處理的吧”。
小貓懵逼了一會(huì),“額?什么是熱點(diǎn)賬戶?我們好像模型里面就一個(gè)資產(chǎn)賬戶,然后充值的時(shí)候和消費(fèi)的時(shí)候更新一下該賬戶,并且記錄一下操作明細(xì),然后結(jié)束了?!?/p>
面試官:“哦?;厝サ韧ㄖ?。”
出來(lái)之后,小貓整個(gè)人都還是懵逼的。

問(wèn)題分析
我們一起來(lái)看一下這樣一個(gè)問(wèn)題,其實(shí)這里面試官想要知道的是,在高并發(fā)的情況下,針對(duì)熱點(diǎn)賬戶如何進(jìn)行賬戶金額的沖扣,小貓沒(méi)有g(shù)et到面試官的點(diǎn),可能他負(fù)責(zé)的項(xiàng)目中本身的量不大,壓根就沒(méi)有想過(guò)這類問(wèn)題。如果問(wèn)的是你,你該如何應(yīng)對(duì)呢?
接下來(lái),咱們一起從以下幾點(diǎn)來(lái)剖析一下這個(gè)問(wèn)題吧。

目錄
什么是熱點(diǎn)賬戶?
熱點(diǎn)賬戶一般指被高頻更新的賬戶,比如短時(shí)間內(nèi)大量的賬戶余額更新請(qǐng)求集中在極少數(shù)賬戶上。這類賬戶雖然數(shù)量不多,但更新頻率很高,處理不當(dāng)可能會(huì)帶來(lái)嚴(yán)重的性能問(wèn)題,影響其他賬戶的正常讀寫操作。
我們來(lái)看一下熱點(diǎn)商家賬戶S的例子,可能更容易讓人理解。大量請(qǐng)求并發(fā)打到我們數(shù)據(jù)庫(kù)底層表的時(shí)候,底層賬戶S究竟發(fā)生了什么。假設(shè)我們現(xiàn)在有用戶A、用戶B、用戶C,等等可能更多,另外有一個(gè)商家賬戶S,我們暫時(shí)枚舉三個(gè),那么當(dāng)其同時(shí)并發(fā)打到底層數(shù)據(jù)庫(kù)的時(shí)候,就有如下圖所示。

數(shù)據(jù)庫(kù)底層更新
上圖中我們可以看到,對(duì)于同一個(gè)商家賬戶S,由于實(shí)際的業(yè)務(wù)需要更新可用賬戶余額,所以單筆沖扣都是在一個(gè)事務(wù)中進(jìn)行的,任何的更新行為都會(huì)對(duì)數(shù)據(jù)庫(kù)上行鎖。由于并發(fā)量大,請(qǐng)求的數(shù)量又多,大家很容易就能想到鎖的等待問(wèn)題會(huì)嚴(yán)重影響性能。
熱點(diǎn)賬戶的分類
上述我們知道了什么是熱點(diǎn)賬戶,并且知道了熱點(diǎn)賬戶產(chǎn)生的原因。其實(shí)熱點(diǎn)賬戶根據(jù)資金的出入可以分成三種。
- 雙頻賬戶 :上圖中我們看到商家賬戶既有出也有入,那其實(shí)這樣的賬戶就可以理解為雙頻賬戶。雙頻賬戶指入賬頻次以及出賬頻次都很高的賬戶。
- 加頻賬戶:大家可能可快就知道了還有純?nèi)胭~頻次高的,那么這個(gè)就是加頻賬戶。
- 減頻賬戶:純出賬的賬戶那么即為減頻賬戶。
針對(duì)這樣的三種賬戶類型,大家其實(shí)可以聯(lián)想一下這些賬戶對(duì)應(yīng)哪些生活中的場(chǎng)景。
熱點(diǎn)賬戶解決方案
透過(guò)現(xiàn)象看本質(zhì),其實(shí)要解決熱點(diǎn)賬戶問(wèn)題,其實(shí)就是解決數(shù)據(jù)庫(kù)壓力過(guò)大,數(shù)據(jù)庫(kù)表更新失敗,執(zhí)行效率過(guò)低的問(wèn)題。那么解決該類問(wèn)題,閣下該如何應(yīng)對(duì)?(其實(shí)小貓如何可以理解面試官的用意,解決方案應(yīng)該可以想到幾個(gè))
1.提升硬件設(shè)備性能
如果數(shù)據(jù)庫(kù)壓力過(guò)大,數(shù)據(jù)庫(kù)執(zhí)行效率低,最簡(jiǎn)單的方式就是調(diào)整硬件設(shè)備唄。把連接池優(yōu)化,把CPU優(yōu)化,把磁盤優(yōu)化,內(nèi)存優(yōu)化等等。在此不展開(kāi)贅述。老貓給他定義了個(gè)別名“大力出奇跡法”。
2.限流法
這種其實(shí)也很簡(jiǎn)單,但是有點(diǎn)粗暴,數(shù)據(jù)庫(kù)壓力大,那么咱們就讓流量少一點(diǎn)打到數(shù)據(jù)庫(kù)層唄。做個(gè)限流不就結(jié)了么。

限流
這種方式無(wú)論是上述那種類型的熱點(diǎn)賬戶,顯然都支持這種優(yōu)化方法。
但是用戶能滿意么?買個(gè)東西老半天,重試好久都是支付失敗,用戶估計(jì)會(huì)跳起來(lái)吧。
這種犧牲用戶體驗(yàn)的方案不是一點(diǎn)用處都沒(méi)有,這種其實(shí)完全可以配合其他方案一起,作為一種最終的兜底方案。
3.預(yù)寫記賬日志(WAL-Write Ahead Log)
很多中間件類似于zk、etcd、es等,包括mysql底層以及很多的操作系統(tǒng)其實(shí)都用到了WAL的思想,感興趣的小伙伴可以找一下相關(guān)資料。我們也借鑒這種思想,mysql在執(zhí)行insert語(yǔ)句的時(shí)候的效率,其實(shí)要比Update執(zhí)行效率高得多,更新的時(shí)候需要獲取讀和寫,但是insert只需要執(zhí)行順序插入即可。因此咱們就有了下面了這樣的設(shè)想方案。

我們先將賬務(wù)明細(xì)插入到MySQL中,再讀取明細(xì),完成賬戶底層的更新動(dòng)作。
- 優(yōu)點(diǎn):實(shí)時(shí)的交易全部是insert賬務(wù)明細(xì),能大大提高入賬速度,賬戶最終更新的頻度我們可以自行把控,減少并發(fā)帶來(lái)的壓力。
- 缺點(diǎn):賬戶更新存在延遲,這樣的話有可能會(huì)造成賬戶透支的風(fēng)險(xiǎn)。
- 適用:所以這種方案加頻類型的熱點(diǎn)賬戶非常適用,但是對(duì)于減頻賬戶以及雙頻賬戶就需要結(jié)合具體業(yè)務(wù)慎重考慮,因?yàn)闀?huì)存在賬戶透支可能。
4.異步削峰緩沖記賬
我們預(yù)寫記賬方式其實(shí)通過(guò)異步把控頻率進(jìn)行更新賬戶,異步削峰模式其實(shí)和上述模式有點(diǎn)類似,說(shuō)到削峰填谷大家很容易就能想到消息隊(duì)列,于是就有了我們下面的這種方案。

采用消息隊(duì)列的方式,可以緩解突然到來(lái)的大流量。消息多的時(shí)候會(huì)堆積在隊(duì)列中,然后被消費(fèi)慢慢更新下去。
- 優(yōu)點(diǎn):避免了突增的流量給系統(tǒng)帶來(lái)的沖擊。
- 缺點(diǎn):賬戶更新并不是及時(shí)的,另外的話,如果程序處理不當(dāng)或者其他原因,會(huì)造成消息丟失,從而造成記賬錯(cuò)誤,同樣也存在賬戶透支風(fēng)險(xiǎn)。
- 適用:對(duì)于加頻類型的賬戶比較適用,對(duì)于減頻賬戶以及雙頻賬戶慎用,同樣也會(huì)存在賬戶透支風(fēng)險(xiǎn)。譬如加頻場(chǎng)景:在B端收單賬戶與業(yè)務(wù)中間賬戶處理;減頻場(chǎng)景在C端微信、頭條等春節(jié)搶紅包入賬處理(注意賬戶透支風(fēng)險(xiǎn))。
5.匯總明細(xì)記賬
關(guān)于該方案其實(shí)思路是這樣的,既然多次頻繁更新賬戶余額成為瓶頸,那么我們就將多次更新統(tǒng)計(jì)之后轉(zhuǎn)換為一次更新。如下圖:

匯總明細(xì)記賬
這種基于統(tǒng)計(jì)之后更新賬戶余額的行為,也是異步進(jìn)行的。所以優(yōu)缺點(diǎn)當(dāng)然也是顯而易見(jiàn)的。
- 優(yōu)點(diǎn):化多次賬戶余額更新操作為一次,減少了數(shù)據(jù)庫(kù)的讀寫操作。
- 缺點(diǎn):由于是異步進(jìn)行,交易其實(shí)并不能實(shí)時(shí)入賬,如果是減頻賬戶場(chǎng)景的話,也還是會(huì)有賬戶透支風(fēng)險(xiǎn)。
- 適用:非常適用加頻熱點(diǎn)賬戶,對(duì)于減頻賬戶以及雙頻賬戶慎用。
6.賬戶拆分記賬
既然單個(gè)賬戶寫入的時(shí)候壓力過(guò)大,那么我們就將單個(gè)熱點(diǎn)賬戶拆分成多個(gè)子賬戶去分散每個(gè)賬戶的讀寫壓力。

賬戶拆分記賬
這種方案只要能夠處理好扣款時(shí),子賬戶余額不夠扣,資金歸集處理得好,那么問(wèn)題其實(shí)也能夠得到很好的解決。
- 優(yōu)點(diǎn):分散了單個(gè)熱點(diǎn)賬戶的寫入量。
- 缺點(diǎn):子賬戶扣款的時(shí)候余額扣款處理需要做資金歸集,可能出現(xiàn)扣款失敗,但是如果歸集做的好,那么其實(shí)問(wèn)題也不大。
- 適用:這種方案適用于所有類型的熱點(diǎn)賬戶類型。
7.緩存記賬
Mysql的讀寫能力不好,那么我們就去尋找比Mysql讀寫效率更高的中間件,將結(jié)果預(yù)先計(jì)算好,那么我們很容易就想到了用非關(guān)系型數(shù)據(jù)庫(kù)作為前置賬戶,比如redis,讓計(jì)算結(jié)果先在redis中計(jì)算完成,然后最終異步flush到mysql中。

redis緩存
- 優(yōu)點(diǎn):緩存的吞吐量遠(yuǎn)遠(yuǎn)高于mysql,而且速度很快。
- 缺點(diǎn):數(shù)據(jù)容易丟失。
- 適用:這種方案適用于熱點(diǎn)賬戶的任意一種類型,但是由于其數(shù)據(jù)準(zhǔn)確性的考慮,往往對(duì)金額不是那么敏感的可以采用該方法,例如刷短視頻得積分這種場(chǎng)景??梢圆捎迷擃惙桨?。






























