天天看點

高并發下,使用者餘額扣減保證一緻

1,可以通過隊列的方式處理;

2,參見: https://m.wang1314.com/doc/webapp/topic/20483105.html

高并發下,餘額扣減一緻性實踐

藏家004  收藏于2018-11-01   轉藏1次

緣起:在高并發的分布式環境下,對于資料的查詢與修改容易引發一緻性問題,本文将分享一種非常簡單但有效的優化方法。

一、業務場景

業務場景為,購買商品的過程要對餘額進行查詢與修改,大緻的業務流程如下:

(1)從資料庫查詢使用者現有餘額 SELECT money FROM t_yue WHERE uid=$uid,不妨設查詢出來的$old_money=100元

高并發下,使用者餘額扣減保證一緻

(2)業務層實施業務邏輯,比如購買一個80元的商品,并且打九折

if($old_money> 80*0.9) $new_money=$old_money-80*0.9=28

高并發下,使用者餘額扣減保證一緻

(3)将資料庫中的餘額進行修改 UPDAtE t_yue SET money=$new_money WHERE uid=$uid

高并發下,使用者餘額扣減保證一緻

在并發量低的情況下,這個流程沒有任何問題,原有金額100元,購買了80元的九折商品(72元),剩餘28元。

二、潛在的問題

在分布式環境中,如果并發量很大,這種“查詢+修改”的業務很容易出現資料不一緻。極限情況下,可能出現這樣的異常流程:

(1)業務1和業務2同時查詢餘額,是100元

高并發下,使用者餘額扣減保證一緻

(2)業務1和業務2進行邏輯計算,算出各自業務的餘額,假設業務1算出的餘額是28元,業務2算出的餘額是38元

高并發下,使用者餘額扣減保證一緻

(3)業務1對資料庫中的餘額先進行修改,設定成28元。

業務2對資料庫中的餘額後進行修改,設定成38元。

高并發下,使用者餘額扣減保證一緻

此時異常出現了,原有金額100元,業務1扣除了72元,業務2扣除了62元,最後剩餘38元。

三、問題原因

高并發環境下,對同一個資料的并發讀(兩邊都讀出餘額是100)與并發寫(一個寫回28,一個寫回38)導緻的資料一緻性問題。

四、原因分析

業務1的寫回:原有金額100,這是一個初始狀态,寫回金額28,理論上隻有在原有金額為100的時候才允許寫回成功,這一步沒問題。

業務2的寫回:的原有金額100,這是一個初始狀态,寫回金額38,理論上隻有在原有金額為100的時候才允許寫回成功,可實際上,這個時候資料庫中的金額已經變為28了,這一步的寫操作不應該成功。

五、簡易解決方案

在set寫回的時候,加上初始狀态的條件compare,隻有初始狀态不變時,才允許set寫回成功,這正是大家常說的“Compare And Set”(CAS),是一種常見的降低讀寫鎖沖突,保證資料一緻性的方法。

六、業務的更新

業務線使用CAS解決高并發時資料一緻性問題,隻需要在進行set操作時,compare一下初始值,如果初始值變換,不允許set成功。

對于上文中的業務場景,隻需要将“UPDAtEt_yue SET money=$new_money WHERE uid=$uid”更新為

“UPDAtE t_yue SETmoney=$new_money WHERE uid=$uid AND money=$old_money”即可。

并發操作發生時:

業務1執行 => UPDAtE t_yue SET money=28 WHERE uid=$uid AND money=100

業務2執行 => UPDAtE t_yue SET money=38 WHERE uid=$uid AND money=100

【這兩個操作同時進行時,隻能有一個執行成功】。

七、怎麼判斷哪個執行成功,哪個執行失敗

set操作,其實無所謂成功或者失敗,業務能通過affect rows得知哪個修改沒有成功:

執行成功的業務,affect rows為1

執行失敗的業務,affect rows為0

八、總結

高并發“查詢并修改”的場景,可以用CAS(Compare and Set)的方式解決資料一緻性問題。對應到業務,即在set的時候,加上初始條件的比對。