緣起
:在高併發的分布式環境下,對於資料的查詢與修改容易引發一致性問題,本文將分享一種非常簡單但有效的優化方法。
一、業務場景
業務場景為,購買商品的過程要對餘額進行查詢與修改,大致的業務流程如下:
(1)從資料庫查詢使用者現有餘額
select money from t_yue where uid=$uid,不妨設查詢出來的$old_money=100元
(2)業務層實施業務邏輯,比如購買乙個80元的商品,並且打九折
(3)將資料庫中的餘額進行修改
在併發量低的情況下,這個流程沒有任何問題,原有金額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-=$money where uid=$uid and money=$old_money
」即可。
併發操作發生時:
業務1執行 => update t_yue set money-=72
where uid=$uid and money=100
業務2執行 => update t_yue set money-=62
where uid=$uid and money=100
【這兩個操作同時進行時,只能有乙個執行成功】。
七、怎麼判斷哪個執行成功,哪個執行失敗
set操作,其實無所謂成功或者失敗,業務能通過affect rows得知哪個修改沒有成功:
執行成功的業務,affect rows為1
執行失敗的業務,affect rows為0
八、總結
高併發「查詢並修改」的場景,可以用cas(compare and set)的方式解決資料一致性問題。對應到業務,即
在set的時候,加上初始條件的比對
。
併發一致性解決方法
在sql中,當對資料庫中的乙個表進行併發操作時,會出現 資料丟失 不可重複讀 髒讀和幻影讀的情況。處理該併發問題的解決方法 1 封鎖 1.1 共享鎖 又稱讀鎖,如果事務a1用共享鎖讀取資料b,a1事務沒有結束,那麼事務a2在a1事務期間,可以 用共享 鎖來讀取資料,但不能用排它鎖來修改資料。1.2 ...
強一致性 弱一致性 最終一致性
這種方式在es等分布式系統中也有體現,可以設定主shard提交即返回成功,或者需要replica shard提交成功再返回。提到分布式架構就一定繞不開 一致性 問題,而 一致性 其實又包含了資料一致性和事務一致性兩種情況,本文主要討論資料一致性 事務一致性指acid 複製是導致出現資料一致性問題的唯...
CAS解決資料一致性問題
什麼是cas?compare and set cas 是一種常見的降低讀寫鎖衝突,保證資料一致性的方法。資料一致性是我們開發時必須注意的問題,特別涉及到錢這塊。這裡舉乙個使用者購物的例子 現有乙個使用者資訊表user account,表中字段為id,uid,amount。表中現有使用者甲,餘額100...