一開始想的解決方案:
A、把select和update合并成一條sql
B、事務(wù),用同一個事務(wù)包裹select+update的操作。
分析:并不是包上事務(wù)就萬事大吉。因?yàn)槿绻瑫r有兩個事務(wù)都分別select到了相同的記錄,那么一樣會發(fā)生有一方的更新會失敗的問題。然后我再想到把事務(wù)的隔離級別設(shè)置為serializable,但是考慮到性能顯然不現(xiàn)實(shí)。
我們知道innodb是支持行鎖的,然后我就去查看看他的幾種鎖:讀共享鎖和寫?yīng)氄兼i。
? 讀共享鎖
是通過SQL的LOCK IN SHARE MODE,例:
Select * from parent where name =‘jones’LOCK IN SHARE MODE;
如果事務(wù)A先獲得了讀共享鎖,那么事務(wù)B仍然可以做讀操作。但是必須等事務(wù)A commit或者roll back之后才可以更新或者刪除加了讀共享鎖的行數(shù)據(jù)。-----但是這種鎖并解決不了我們的問題,因?yàn)槲覀儜?yīng)該要再查詢的時候就查到事務(wù)A commit或者roll back后的數(shù)據(jù)才是,才是數(shù)據(jù)別更新后的最新數(shù)據(jù)。
------如果想要解決上面的并發(fā)問題,采用讀共享鎖是不可以解決的
? 寫?yīng)氄兼i
通過SQL的select…for update獲得,例:
Select xxx from xxx for update;
Update xxx set xxx = #{xxx};
如果事務(wù)A先獲得了某行寫共享鎖,那么事務(wù)B就必須等待事務(wù)A commit或者roll back之后才可以訪問數(shù)據(jù)。
------如果想要解決上面的并發(fā)問題,采用寫?yīng)氄兼i是可以解決的
另外這里特別提醒下:UPDATE/DELETE SQL盡量帶上WHERE條件并在WHERE條件中設(shè)定索引過濾條件,否則會鎖表,性能可想而知有多差了。因?yàn)?/h4> MySQL InnoDB默認(rèn)Row-Level Lock,所以只有「明確」地指定主鍵,MySQL 才會執(zhí)行Row lock (只鎖住被選取的數(shù)據(jù)) ,否則MySQL 將會執(zhí)行Table Lock (將整個數(shù)據(jù)表單給鎖住)。
但是寫?yīng)氄兼i是一種悲觀鎖機(jī)制,所以在大佬的指導(dǎo)下還有一種方式,可以避免悲觀鎖,就是樂觀鎖!
·樂觀鎖,類似CAS機(jī)制(應(yīng)該采取的方式)
其實(shí)也很簡單,首先在select的SQL不作任何修改,然后在update的SQL的where條件中加上select出來的。但是避免不了ABA問題。