1. 程式人生 > >熱點賬戶衝扣設計方案

熱點賬戶衝扣設計方案

 

前言:方案設計前提

一般賬務系統對賬戶的衝扣需要滿足以下兩點

1:更新賬戶表中的賬戶餘額。

2:記錄賬戶明細表中的賬戶更新前餘額,賬戶更新後餘額,操作金額。

 其中對賬戶表中的餘額更新一般是直接update,對賬戶明細表中的操作前金額,操作後金額和操作金額就是對賬戶表update的記錄

1:為什麼做熱點賬戶設計

熱點賬戶交易是效能瓶頸,在銀行或者第三方支付系統的賬務資料庫的處理中,資料從一個賬戶轉出,或者有資料轉入一個賬戶,賬戶都會收到記賬請求,並都有一個記賬處理的過程。記賬處理過程主要包括兩部分,一是記錄記賬憑證,二是更新賬戶的餘額。為了保證賬戶不被其他請求影響資料的準確性,在進行記賬處理時,會先對賬戶的資源加鎖,記賬處理完畢後會自動釋放鎖。隨著賬務處理業務量的增大,賬務資料庫中的賬戶常常會在瞬間產生多個併發操作,但所有對應的併發執行緒中只有一個執行緒能夠持有當前賬戶的資源鎖,其他執行緒必須等待該鎖被釋放後再逐一進行記賬處理,這樣該賬戶將會被頻繁加鎖釋鎖,使該賬戶成為賬務資料庫熱點,產生效能瓶頸點,嚴重影響賬務資料庫的效能。

 

對於同一賬戶ID來說,由於實際業務需要更新賬戶可用餘額和賬戶流水日誌,所以單筆衝扣功能是在一個事物中進行操作,任何更新操作都會對資料上行鎖,圖例如下

 

 

2:業界關於熱點賬戶衝扣設計方案

 

1.併發度控制

同一時刻,對同一賬戶修改的請求數越多,這個賬戶的所等待問題就越嚴重,所謂併發度控制就是要控制同一時刻對熱點賬戶請求的數量,可以通過控制上游支付系統併發請求資料或者賬務系統處理的併發請求數來實現。這一方案的缺點是對業務是有損的,當熱點賬戶出現的時候,支付或者賬務處理失敗率會增加,使用者的體驗會變差,較大的銀行或者第三方支付公司用地比較少。

 

 

2.彙總明細記賬

實時的交易全部是insert賬務明細(insert的開銷很小,能夠支援高併發。如果基於分散式部署,insert的併發容量理論上可以無限大),然後定時(比如每半個小時)將之前半個小時內的賬務明細sum出一個結算總金額,一筆入賬結算到指定賬戶。這個方案的缺點就是:交易不能實時入賬,其實如果控制好定時彙總入賬的頻度,比如分鐘級,使用者也是可以接受的。這種方式對收單類業務(賬戶加錢)非常實用,但是對支出類業務(賬戶減錢)類來說,有賬戶透支地風險。

 

 

3.緩衝入賬

將實時同步的記賬請求進行非同步化,以達到記賬實時性和系統穩定性之間平衡的記賬手段,這就是”削峰填谷“。詳細地講,假如賬務系統對同一個賬戶的處理閾值為100筆/s,24小時不間斷服務(一天能處理86400000筆)。當業務高峰期來臨的時候,熱點賬務的請求數會達到200筆/s。當賬戶的交易低於100筆/秒的時候,賬務系統幾乎還是實時地處理了記賬請求,而當交易大於100筆/秒的時候,賬務系統先返回結果,把賬務處理丟到可靠的處理佇列中,等併發量不大的時候慢慢消化,對使用者來說感受到的體驗還是很快就記賬成功了。   這個方案是有個前提是:熱點賬戶在某幾個高峰時間點需要緩衝記賬來削峰填谷,並且能在日間填完。一旦賬戶的日間交易量暴增,導致日間佇列根本來不及消化,整個佇列越來越長,那就不存在谷可以填,這時候肯定會帶來使用者大量的投訴。另外這種方案對支出類業務(賬戶減錢)來講,也會有賬戶透支地風險

 

 

 

4.子賬戶拆分

具體來講就是建立與熱點賬戶對應的多個影子賬戶,所述影子賬戶與所述賬戶的資料結構相同,將所述影子賬戶設定為隱藏,並將所述賬戶的餘額分散至各個影子賬戶。當賬務系統接收到賬務請求的時候,通過前置進行hash分配(具體的hash函式會有更多方案)選擇影子賬戶進行記賬,這樣就將原來對一個賬戶的請求分散到多個影子賬戶中,分散了賬務熱點。   這個方案也有缺點:通過演算法選擇的影子賬戶扣款,影子賬戶的餘額可能是不足的,但賬戶的總餘額是夠的,這樣可能影響賬務處理的成功率。

 

 

5.記憶體資料庫+快取入賬

提高單臺數據庫伺服器處理能力(I/O,CPU,memory)或者選取記憶體資料庫實時地處理記賬請求,然後非同步地儲存到可靠資料庫上。

 

 

6.升級服務硬體,對CPU記憶體等進行升級

3:幾種方案的對比

1:併發度控制

對單個賬戶併發操作進行限流降級控制,使得系統健康的完成入賬出賬操作,但是在併發很高的情況下還是會殺死很多正常的衝扣功能,會極大的提高衝扣的失敗率,所以對我們賬務系統來說不是允許的。

2:彙總明細入賬

對賬戶的衝扣操作已流水的形式記錄下來,通過定時job來將出入賬流水更新到業務表中。這種做法對於頻繁的入賬來說效能提高明顯,但是因為沒有對總金額進行校驗,對支出類業務(賬戶減錢)類來說,有賬戶透支地風險。並且對於金額的校驗需要通過流水資料和當前可用餘額來判定,有併發問題,計算很難準確。並且我們實際線上業務是【頻繁出賬,低頻入賬】,所以此辦法不可取。

3:緩衝入賬

需要動態判斷流量低峰高峰,維護請求佇列,有賬戶透支地風險,並且非同步請求中結果不可控。

4:子賬戶拆分

子賬戶拆分方案中對於子賬戶的扣款進行負載,可以滿足對同一賬戶的高頻訪問負載到其子賬戶上,極大滿足了併發的需求,子賬戶的餘額可能是不足的,但賬戶的總餘額是夠的,這樣可能影響賬務處理的成功率,並且處理對子賬戶的扣款和入賬來說需要做到金額相對平均比較複雜,對記錄賬戶期初餘額期末餘額處理涉及到併發,相對複雜。

5:增加硬體處理能力CPU.記憶體等

備選方案,無法從根本上解決單點賬戶的併發壓力。

6:記憶體資料庫實時地處理記賬,非同步入庫

使用redis做資料前置處理,將資料庫中的熱點賬戶金額初始同步到redis中,然後將操作記錄流水,通過job定時任務重新整理流水到業務表。這樣將db和快取分開極大的加大了併發效能,但是卻衍生出來一個問題如下

假設redis初始金額為100,

(1) 當執行緒1對redis賬戶金額進行原子減操作時,剩餘金額40,並記錄流水錶等待非同步入賬

(2) 當執行緒2對redis賬戶金額進行原子減操作時,剩餘金額-20,此時金額已經為負,按照業務要求金額不能為負所以必須要做反向操作

(3) 當執行緒2還沒有對redis餘額進行反向操作維護的時候又出現執行緒3進行充值操作,此時金額又變成-20+100=80,已經出現金額混亂,對業務要求的期初餘額期末餘額無法準確的滿足,所以對redis的金額進行同時衝扣會帶來餘額的併發問題。

 

但是對快取進行操作和延遲批量流水入賬可以極大的滿足我們對效能的需求,所以在【2.彙總明細記賬】和【5.記憶體資料庫+快取入賬】的基礎上進行改良來滿足對我們的業務需求

 

4:詳細方案設計

方案設計前提:

(1):【對賬戶的餘額的更新】:準確的更新賬戶餘額,不允許出現多扣,少扣等情況。

(2):【對賬戶操作記錄的更新】:準確的記錄賬戶流水錶中期初餘額,期末餘額,操作金額等情況,不允許出現任何的金額錯誤發生。

 

前期準備:

(1) :新增延遲入賬【流水錶】,新增入賬,出賬資料先入【流水錶】,通過定時任務將【流水錶】入賬和出賬資料同步到業務資料表中,並且負責新增入賬資料的快取同步工作。

下面的方案會對此表統一稱為【流水錶】

 

 

(2) :新增【redis】資料結構【SortedSet(有序集合)】 key為【hotspot_account】

 

下面會對這個資料集合稱為【快取操作記錄】

 

其中score為當前賬戶操作時間【新覆蓋舊】,member為出入賬的賬戶ID。key【hotspot_account】,所有賬戶的入賬出賬操作需要記錄到hotspot_account中,主要是提供給【圖1中定時任務】獲取所有賬戶流水ID。

 

(3)新增【redis】資料結構【SortedSet(有序集合)】 key為【hotspot_account_currentbalance】

下面會對這個資料集合稱為【快取餘額】

 

 

其中:

score為當前賬戶可用餘額,【熱點賬戶新操作流程之前需要將資料庫中熱點賬戶的資料同步到hotspot_account_currentbalance中】

member為賬戶ID

 

到此,前期準備工作已經全部結束。

 

當賬戶金額充值新增時:

1:記錄redis操作記錄【hotspot_account】

 

如圖所示紅色資料部分,當賬戶110000056666660010入賬時,插入或更新資料,member=110000056666660010,score為當前時間戳(秒)。

ps:操作指令【ZINCRBY key increment member】,當 key 不存在,或 member 不是 key 的成員時, ZINCRBY key increment member 等同於 ZADD key increment member 。

2:新增【流水錶】,設定入賬狀態為未入賬

 

 

當賬戶金額扣減時:

1:同金額充值相同首先記錄redis操作記錄【hotspot_account】。

2:直接對快取hotspot_account_currentbalance對應的金額進行扣減。

 

3:定時任務

定時任務的作用是將流水錶的資料更新到【賬戶表】,和【流水明細表】,並且設定【流水錶中】資料已入賬,同時要將新入賬資料流水到更新【hotspot_account_currentbalance】中的可用賬戶餘額,讓扣減操作得以繼續進行。以下操作流程:

 

 

關於任何問題請私信我

或者加入高階java群探討 QQ群號:825199617