拿捏!隔離級別、幻讀、Gap Lock、Next-Key Lock

源 /艾小仙 文/ 艾小仙
鎖

隔離級別

幻讀、Next-Key Lock、MVCC
The so-called phantom problem occurs within a transaction when the same query produces different sets of rows at different times. For example, if a SELECTis executed twice, but returns a row the second time that was not returned the first time, the row is a “phantom” row.
select執(zhí)行了兩次,但是第二次比第一次多出來行記錄,這就是幻讀。select * from user where age=10 for update,得到的結(jié)果是id為[1,2,3]的記錄,再次執(zhí)行查詢,得到了結(jié)果為[1,2,3,4]的記錄,這是幻讀。select查詢,查詢的都是快照版本,這個場景下因為都是基于MVCC來查詢快照的某個版本,所以不會存在幻讀的問題,也可以認(rèn)為是解決了幻讀的方案之一,對于RC級別來說,因為每次查詢都重新生成一個read view,也就是查詢的都是最新的快照數(shù)據(jù),所以會可能每次查詢到不一樣的數(shù)據(jù),造成不可重復(fù)讀,而對于RR級別來說只有第一次的時候生成read view,查詢的是事務(wù)開始的時候的快照數(shù)據(jù),所以就不存在不可重復(fù)讀的問題,當(dāng)然就更不可能有幻讀的問題了。lock in share mode、for update 、insert、update、delete這些需要加鎖的操作。對于MVCC來說就是解決的快照讀的場景,而對于當(dāng)前讀那么就是Next-Key Lock要解決的事情。select * from user where age=10 for update,將會鎖住user表所有age=10的行記錄,所有對age=10的記錄的操作都會被阻塞。select * from user where age>1 and age<10 for update,將會鎖住age在(1,10)的范圍區(qū)間,此時其他事務(wù)對該區(qū)間的操作都會被阻塞。事務(wù)隔離級別設(shè)置為讀已提交RC ,這樣肯定沒有間隙鎖了。 Innodb_locks_unsafe_for_binlog設(shè)置為1另外一種情況適用于主鍵索引或者唯一索引的等值查詢條件,比如 select * from user where id=1,id是主鍵索引,這樣只使用Record Lock就可以了,因為能唯一鎖定一條記錄,所以沒有必要再加間隙鎖了,這是鎖降級的過程。
解決幻讀

首先第一步Server層會來查詢數(shù)據(jù) 存儲引擎根據(jù)查詢條件查到數(shù)據(jù)之后對數(shù)據(jù)進(jìn)行加鎖,Record Lock或者間隙鎖,然后返回數(shù)據(jù) Server層拿到數(shù)據(jù)之后調(diào)用API去存儲引擎更新數(shù)據(jù) 最后存儲引擎返回結(jié)果,流程結(jié)束
user表有4個字段,id是主鍵索引,name是唯一索引,age是普通索引,city沒有索引,然后插入一些測試數(shù)據(jù),下面區(qū)分一下幾種情況來說明是怎么加Next-Key Lock的,然后就知道為啥會沒有幻讀的問題了。
沒有索引
update user set city='nanjing' where city='wuhan'會發(fā)生什么?city是沒有索引的,所以存儲引擎只能給所有的記錄都加上鎖,然后把數(shù)據(jù)都返回給Server層,然后Server層把city改成nanjing,再更新數(shù)據(jù)。
普通索引
select * from user where age=20 for update。age是一個普通索引,存儲引擎根據(jù)條件過濾查到所有匹配age=20的記錄,給他們加上寫鎖,間隙鎖會加在(10,20),(20,30)的區(qū)間上,因此現(xiàn)在無論怎樣都無法插入age=20的記錄了id=11,age=20或者id=21,age=20的記錄,這樣就存在幻讀了。age普通索引上,還會加在主鍵索引上,因為數(shù)據(jù)都是在主鍵索引下對吧,這個肯定也要加鎖的,為了看起來簡單點,就不畫出來了)
唯一&主鍵索引
select * from user where name='b' for update。name=b的的記錄的,也不存在幻讀問題。id>1 and id<11呢,實際上也是一樣的鎖定方式,不再贅述。
總結(jié)


好文推薦

某程序員吐槽:太尷尬!四年不見的前女友來公司面試,自己還是面試官!

“要源碼上門自取”,結(jié)果人真上門了!國內(nèi)企業(yè)再惹爭議

有個程序員老公該多爽???
END


頂級程序員:topcoding
做最好的程序員社區(qū):Java后端開發(fā)、Python、大數(shù)據(jù)、AI
一鍵三連「分享」、「點贊」和「在看」
評論
圖片
表情
