用CAS方案解決高併發一致性問題

詳見:http://blog.yemou.net/article/query/info/tytfjhfascvhzxcyt395數據庫

 

緣起:在高併發的分佈式環境下,對於數據的查詢與修改容易引起一致性問題,本文將分享一種很是簡單但有效的優化方法。併發

1、業務場景分佈式

業務場景爲,購買商品的過程要對餘額進行查詢與修改,大體的業務流程以下:高併發

(1)從數據庫查詢用戶現有餘額 SELECT money FROM t_yue WHERE uid=$uid,不妨設查詢出來的$old_money=100元優化

 

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

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

 

(3)將數據庫中的餘額進行修改 UPDAtE t_yue SET money=$new_money WHERE uid=$uidblog

 

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

2、潛在的問題方法

在分佈式環境中,若是併發量很大,這種「查詢+修改」的業務很容易出現數據不一致。極限狀況下,可能出現這樣的異常流程:

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

 

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

 

(3)業務1對數據庫中的餘額先進行修改,設置成28元。

業務2對數據庫中的餘額後進行修改,設置成38元。

 

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

3、問題緣由

高併發環境下,對同一個數據的併發讀(兩邊都讀出餘額是100)與併發寫(一個寫回28,一個寫回38)致使的數據一致性問題。

4、緣由分析

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

業務2的寫回:的原有金額10038100的時候才容許寫回成功,可實際上,這個時候數據庫中的金額已經變爲28了,這一步的寫操做不該該成功。

5、簡易解決方案

在set寫回的時候,加上初始狀態的條件compare,只有初始狀態不變時,才容許set寫回成功,這正是你們常說的「Compare And Set」(CAS),是一種常見的下降讀寫鎖衝突,保證數據一致性的方法。

6、業務的升級

業務線使用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執行SET money=38 WHERE uid=$uid

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

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

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

執行成功的業務,爲1

執行失敗的業務,爲0

8、總結

高併發「查詢並修改」的場景,能夠用CAS(Compare and Set)的方式解決數據一致性問題。對應到業務,即在set的時候,加上初始條件的比對。

相關文章
相關標籤/搜索