天天看點

并發扣款一緻性優化,CAS下ABA問題,這個話題還沒聊完!!!

上一篇答星球水友提問, 《并發扣款,如何保證資料的一緻性?》

中提到:用CAS樂觀鎖,可以在盡量不影響吞吐量的情況下,保證資料的一緻性。

大家有非常多的留言,大概有這麼幾類:(1)是否存在ABA問題?(2)為什麼不能用:UPDATE t_yue SET money=money-$diff AND money>=$diff;(3)能否借助redis事務來扣減餘額;

問題比較多,今天先聊第一個問題,ABA。

 什麼是ABA問題?

CAS樂觀鎖機制确實能夠提升吞吐,并保證一緻性,但在極端情況下可能會出現ABA問題。

考慮如下操作:

  • 并發1(上):擷取出資料的初始值是A,後續計劃實施CAS樂觀鎖,期望資料仍是A的時候,修改才能成功
  • 并發2:将資料修改成B
  • 并發3:将資料修改回A
  • 并發1(下):CAS樂觀鎖,檢測發現初始值還是A,進行資料修改

 上述并發環境下,并發1在修改資料時,雖然還是A,但已經不是初始條件的A了,中間發生了A變B,B又變A的變化,此A已經非彼A,資料卻成功修改,可能導緻錯誤,這就是CAS引發的所謂的ABA問題。 餘額操作,出現ABA問題并不會對業務産生影響,因為對于“餘額”屬性來說,前一個A為100餘額,與後一個A為100餘額,本質是相同的。

但其他場景未必是這樣,舉一個堆棧操作的例子:

并發扣款一緻性優化,CAS下ABA問題,這個話題還沒聊完!!!

并發1(上):讀取棧頂的元素為“A1”

并發扣款一緻性優化,CAS下ABA問題,這個話題還沒聊完!!!

并發2:進行了2次出棧

并發扣款一緻性優化,CAS下ABA問題,這個話題還沒聊完!!!

并發3:又進行了1次出棧

并發扣款一緻性優化,CAS下ABA問題,這個話題還沒聊完!!!

并發1(下):實施CAS樂觀鎖,發現棧頂還是“A1”,于是修改為A2

并發扣款一緻性優化,CAS下ABA問題,這個話題還沒聊完!!!

此時會出現系統錯誤,因為此“A1”非彼“A1” ABA問題可以怎麼優化?ABA問題導緻的原因,是CAS過程中隻簡單進行了“值”的校驗,再有些情況下,“值”相同不會引入錯誤的業務邏輯(例如餘額),有些情況下,“值”雖然相同,卻已經不是原來的資料了(例如堆棧)。 是以,CAS不能隻比對“值”,還必須確定是原來的資料,才能修改成功。 常見的實踐是,将“值”比對,更新為“版本号”的比對,一個資料一個版本,版本變化,即使值相同,也不應該修改成功。 餘額并發讀寫例子,引入版本号的具體實踐如下:(1)餘額表要更新。t_yue(uid, money)更新為:t_yue(uid, money, version) (2)查詢餘額時,同時查詢版本号。SELECT money FROM t_yue WHERE sid=$sid更新為:SELECT money,version FROM t_yue WHERE sid=$sid假設有并發操作,都會将版本号查詢出來。 (3)設定餘額時,必須版本号相同,并且版本号要修改。舊版本“值”比對:UPDATE t_yue SET money=38 WHERE uid=$uid AND money=100更新為“版本号”比對:UPDATE t_yue SET money=38, version=$version_new WHERE uid=$uid AND version=$version_old

此時假設有并發操作,首先操作的請求會修改版本号,并發操作會執行失敗。畫外音:version通用,本例是強行用version舉例而已,實際上本例可以用餘額“值”比對。 總結

  • select&set業務場景,在并發時會出現一緻性問題
  • 基于“值”的CAS樂觀鎖,可能導緻ABA問題
  • CAS樂觀鎖,必須保證修改時的“此資料”就是“彼資料”,應該由“值”比對,優化為“版本号”比對

思路比結論重要。

本文轉自“架構師之路”公衆号,58沈劍提供。