排它鎖(寫(xiě)鎖) For Update
作用
使用for update 為所有查詢select的記錄加上獨(dú)占鎖蹬蚁。
獨(dú)占鎖又叫寫(xiě)鎖,意思在鎖的期間辞嗡,不允許其他任何嘗試獲取鎖(包括讀鎖和寫(xiě)鎖)的請(qǐng)求,只有這個(gè)鎖被釋放掉才能被另外一個(gè)事務(wù)獲取鎖匈子。
示例
事務(wù) T1 先執(zhí)行如下語(yǔ)句
begin;
select * from trans where id=2 for update; -- 這里對(duì)id=2的記錄進(jìn)行加鎖(排它鎖)
-- 注意這里還沒(méi)有結(jié)束事務(wù),會(huì)一直占用著id=2記錄的鎖
事務(wù) T2 在以上語(yǔ)句執(zhí)行后 執(zhí)行如下語(yǔ)句:
begin; -- 即使沒(méi)有開(kāi)始事務(wù),下面的語(yǔ)句也會(huì)被一直阻塞
select * from trans; -- 這里只是簡(jiǎn)單請(qǐng)求查詢,并沒(méi)有請(qǐng)求獲取鎖代乃,所以這里不會(huì)被阻塞旬牲,不需要等待事務(wù)T1結(jié)束事務(wù)
select * from trans for update; -- 這里查詢包含id=2的記錄,嘗試獲取排它鎖搁吓,但由于事務(wù)T1還沒(méi)有結(jié)束事務(wù)原茅,一直占用著id=2的行鎖,所以這里會(huì)一直等待獲取鎖
update trans set xxx=xxx where id=2; -- update語(yǔ)句也會(huì)嘗試申請(qǐng)寫(xiě)鎖堕仔,所以這里也一樣被阻塞擂橘,等待事務(wù)T1的鎖
commit;
應(yīng)用場(chǎng)景
使用for update 可以解決臟讀問(wèn)題,例如:
有兩個(gè)定時(shí)任務(wù)(可以當(dāng)作兩個(gè)事務(wù))分別為Job-1 和 Job-2, 這兩個(gè)Job都會(huì)修改同一條記錄,可能出現(xiàn)其中一個(gè)Job 讀取這條記錄摩骨,是另外一個(gè)Job準(zhǔn)備要修改的通贞,等另外一個(gè)Job修改完朗若,這個(gè)Job讀取的這條記錄已經(jīng)過(guò)時(shí),如下執(zhí)行順序(就當(dāng)下面是44拍樂(lè)譜里的一小節(jié)):
① ④
Job-1: | ======= | ======= | ======= | ======= |
② ③
Job-2: | ======= | ======= | ======= | ======= |
注解:
順序① : 執(zhí)行查詢語(yǔ)句昌罩,準(zhǔn)備下一步更新
-- Job-1語(yǔ)句
begin;
select total from xxx where id=2; -- 這里查詢id=2記錄的total,得出total值為3
順序②: Job-2 更新Job-1 在順序-1中查詢的記錄
-- Job-2語(yǔ)句
begin;
update xxx set total=10 where id=2; -- 這里修改id=2記錄的total值為10
順序③: Job-2 結(jié)束事務(wù)
-- Job-2語(yǔ)句
commit;
順序④: 使用過(guò)期數(shù)據(jù)total進(jìn)行累加遞增
-- Job-1語(yǔ)句
update xxx set total=3+10 where id=2; -- 由于這里的3已經(jīng)過(guò)時(shí)了哭懈,導(dǎo)致total與實(shí)際的10相差了7,實(shí)際累計(jì)之后應(yīng)該是 10 + 10 = 20
commit;
解決
為了解決Job-1讀取的id=2記錄的total值不過(guò)時(shí),所以在順序①查詢id=2時(shí)通過(guò)for update 進(jìn)行加鎖即可:
-- Job-1語(yǔ)句
select * from trans where id=2 for update;
或者使用共享鎖:
select * from trans where id=2 lock in share mode;
關(guān)于共享鎖請(qǐng)往下看茎用。
.
.
.
.
.
.
.
.
共享鎖(讀鎖) Select ... lock in share mode
作用
對(duì)select 查詢的所有記錄加上共享鎖(或者說(shuō)獲取這些記錄的共享鎖)
允許其他事務(wù)也加上共享鎖
但不允許其他事務(wù)加上寫(xiě)鎖
共享鎖的特征
別名
共享鎖又讀鎖-
何謂"共享"?
所謂共享的意思是如果事務(wù) A 獲取了共享鎖, 允許事務(wù)B 也獲取共享鎖遣总,即允許事務(wù)B 可以讀取相同的數(shù)據(jù),就是這樣, 對(duì)另外一個(gè)獲取“共享鎖”的事務(wù)共享當(dāng)前正在讀的數(shù)據(jù)轨功。但對(duì)寫(xiě)鎖卻是“排它”的旭斥,意思在獲取的讀鎖的時(shí)候不允許寫(xiě)鎖進(jìn)行寫(xiě)數(shù)據(jù),要不然就會(huì)出現(xiàn)臟讀(讀到的數(shù)據(jù)因?yàn)楸桓亩^(guò)時(shí))古涧。
實(shí)現(xiàn)原理:
有一個(gè)先進(jìn)先出的隊(duì)列,隊(duì)列中存放的都是所有鎖垂券,包括共享鎖 和 排它鎖。如下FIFO隊(duì)列示意圖, 按照從左到右的先進(jìn)先出順序存放各種鎖:
列頭 < ①Read Lock == ②Read Lock < ③Write Lock < ④Read Lock < 列尾
注意:上圖隊(duì)列中 讀鎖① 和 讀鎖② 中間用等號(hào)羡滑,表示這兩個(gè)鎖可以同時(shí)進(jìn)行數(shù)據(jù)查詢菇爪。其余的小于符號(hào)表示 右邊的鎖等待小于符號(hào)左邊的鎖。
上圖隊(duì)列中一共有4個(gè)鎖啄栓,分別為讀鎖①娄帖、讀鎖②也祠、寫(xiě)鎖③昙楚、讀鎖④。并且每個(gè)鎖都分別對(duì)用有1個(gè)事務(wù)進(jìn)行獲取诈嘿,即有4個(gè)事務(wù)堪旧。
事務(wù)1(讀鎖)
select * from trans where id=2 lock in share mode;
事務(wù)2(讀鎖)
select * from trans where id=2 lock in share mode;
事務(wù)3(寫(xiě)鎖)
update trans set xxx=10 where id=2; -- 加上寫(xiě)鎖(獨(dú)占鎖)
或
select * from trans where id=2 for update; -- 加上獨(dú)占鎖。
事務(wù)4(讀鎖)
select * from trans where id=2 lock in share mode;
請(qǐng)求鎖順序:
- 事務(wù)獲得①讀鎖奖亚。
- 事務(wù)2嘗試獲取讀鎖②淳梦,發(fā)現(xiàn)前面已經(jīng)有①鎖并且是一個(gè)讀鎖,這時(shí)候獲取成功,并可以讀取到共享鎖正在讀取的數(shù)據(jù)昔字。
- 事務(wù)3嘗試用獲取寫(xiě)鎖③爆袍,但發(fā)現(xiàn)前面已經(jīng)有兩個(gè)寫(xiě)鎖,所以等待直至讀鎖釋放掉作郭。
- 事務(wù)4常使用獲取 讀鎖④陨囊,發(fā)現(xiàn)前面的是寫(xiě)鎖③,所以等待事務(wù)3釋放寫(xiě)鎖③,而寫(xiě)鎖③正在等待前面兩個(gè) 讀鎖①夹攒、讀鎖②蜘醋,所以事務(wù)4間接在等待 最前面兩個(gè)讀鎖。
參考
可以參考 Zookeeper 中的共享鎖及獨(dú)占鎖咏尝,原理基本上跟上面說(shuō)的一樣压语。