一、并發(fā)業(yè)務場景
庫存業(yè)務,stock(sid, num),其中:
?sid為庫存id
?num為庫存值

如上圖所示,兩個
并發(fā)的查詢庫存操作,同時從數(shù)據(jù)庫都得到了庫存是5。
接下來用戶發(fā)生了
并發(fā)的庫存扣減動作:

如上圖所示:
?用戶1購買了3個庫存,于是庫存要設置為2
?用戶2購買了2個庫存,于是庫存要設置為3
這兩個設置庫存的接口并發(fā)執(zhí)行,
庫存會先變成2,再變成3,導致數(shù)據(jù)不一致(實際賣出了5件商品,但庫存只扣減了2,最后一次設置庫存會覆蓋和掩蓋前一次并發(fā)操作)
二、不一致原因分析
出現(xiàn)數(shù)據(jù)不一致的根本原因,是設置操作發(fā)生的時候,沒有檢查庫存與查詢出來的庫存有沒有變化,理論上:
?僅庫存為5的時候,用戶1的庫存設置2才能成功
?僅庫存為5的時候,用戶2的庫存設置3才能成功
實際執(zhí)行的時候:
?庫存為5,用戶1的set stock 2確實應該成功
?庫存變?yōu)?了,用戶2的set stock 3應該失敗掉
三、CAS優(yōu)化
大家常說的“Compare And Set”(CAS),是一種常見的降低讀寫鎖沖突,保證數(shù)據(jù)一致性的樂觀鎖機制。
針對上述庫存扣減的例子,CAS升級很容易,將庫存設置接口執(zhí)行的SQL:
update stock set num=$num_new where sid=$sid
升級為:
update stock set num=$num_new where sid=$sid
and num=$num_old即可。
四、什么是ABA問題
CAS樂觀鎖機制確實能夠提升吞吐,并保證一致性,但在極端情況下可能會出現(xiàn)ABA問題。
什么是ABA問題?考慮如下操作:
?并發(fā)1(上):獲取出數(shù)據(jù)的初始值是A,后續(xù)計劃實施CAS樂觀鎖,期望數(shù)據(jù)仍是A的時候,修改才能成功
?并發(fā)2:將數(shù)據(jù)修改成B
?并發(fā)3:將數(shù)據(jù)修改回A
?并發(fā)1(下):CAS樂觀鎖,檢測發(fā)現(xiàn)初始值還是A,進行數(shù)據(jù)修改
上述并發(fā)環(huán)境下,
并發(fā)1在修改數(shù)據(jù)時,雖然還是A,但已經(jīng)不是初始條件的A了,中間發(fā)生了A變B,B又變A的變化,此A已經(jīng)非彼A,數(shù)據(jù)卻成功修改,可能導致錯誤,這就是CAS引發(fā)的所謂的ABA問題。
庫存操作,出現(xiàn)ABA問題并不會對業(yè)務產(chǎn)生影響。
再看一個堆棧操作的例子:

并發(fā)1(上):讀取棧頂?shù)脑貫椤癆1”

并發(fā)2:進行了2次出棧

并發(fā)3:又進行了1次出棧

并發(fā)1(下):實施CAS樂觀鎖,
發(fā)現(xiàn)棧頂還是“A1”,于是修改為A2
此時會出現(xiàn)
系統(tǒng)錯誤,
因為此“A1”非彼“A1”五、ABA問題的優(yōu)化
ABA問題導致的原因,是CAS過程中只
簡單進行了“值”的校驗,再有些情況下,“值”相同不會引入錯誤的業(yè)務邏輯(例如庫存),有些情況下,“值”雖然相同,卻已經(jīng)不是原來的數(shù)據(jù)了。
優(yōu)化方向:CAS不能只比對“值”,
還必須確保的是原來的數(shù)據(jù),才能修改成功。
常見實踐:
“版本號”的比對,一個數(shù)據(jù)一個版本,版本變化,即使值相同,也不應該修改成功。
庫存的并發(fā)讀寫例子,引入版本號的具體實踐如下:
(1)庫存表由
stock(sid, num)
升級為
stock(sid, num,
version)
(2)查詢庫存時同時查詢版本號
select num from stock where sid=$sid
升級為
select num,
version from stock where sid=$sid

假設有并發(fā)操作,都會將版本號查詢出來
(3)設置庫存時,必須版本號相同,并且版本號要修改
舊版本
“值”比對CAS
update stock set num=$num_new where sid=$sid and
num=$num_old升級為
“版本號”比對CAS
update stock set num=$num_new,
version=$version_new where sid=$sid and
version=$version_old
此時假設有并發(fā)操作,第一個操作,比對版本號成功,于是把庫存和版本號都進行了修改。

同時存在的第二個并發(fā)操作,比對版本號發(fā)生了變化,也是庫存應該修改失敗。
六、總結
?select&set業(yè)務場景,在并發(fā)時會出現(xiàn)一致性問題
?基于“值”的CAS樂觀鎖,可能導致ABA問題
?CAS樂觀鎖,必須保證修改時的“此數(shù)據(jù)”就是“彼數(shù)據(jù)”,應該由“值”比對,優(yōu)化為“版本號”比對
更多建議: