一. 鎖們
二. 什么是間隙鎖唬滑?
間隙鎖(Gap Lock):間隙鎖是一個在索引記錄之間的間隙上的鎖,可以是兩個索引記錄之間,也可能是第一個索引記錄之前或最后一個索引之后的空間别凤。
當(dāng)我們用范圍條件而不是相等條件索引數(shù)據(jù)枉层,并請求共享或排他鎖時,InnoDB會給符合條件的已有數(shù)據(jù)記錄的索引項枷鎖届宠;對于鍵值在條件范圍內(nèi)但并不存在的記錄烁落,叫做“間隙(GAP)”。
InnoDB也會對這個“間隙”枷鎖豌注,這種鎖機制就是所謂的間隙鎖(Next-Key鎖)伤塌。
三. 間隙鎖的危害
因為Query執(zhí)行過程中通過范圍查找的話,他會鎖定整個范圍內(nèi)所有的索引鍵值轧铁,即使這個鍵值并不存在每聪。間隙鎖有一個比較致命的弱點,就是當(dāng)鎖定一個范圍鍵值之后齿风,即使某些不存在的鍵值也會被無辜的鎖定药薯,也造成在鎖定的時候無法插入鎖定鍵值范圍內(nèi)的任何數(shù)據(jù)。在某些場景下這可能會對性能造成很大的危害救斑。
四. 間隙鎖與死鎖
最近用戶反饋說系統(tǒng)老是出現(xiàn)insert時童本,等待超時了,最后發(fā)現(xiàn)是insert間隙鎖脸候!間隙鎖是innodb中行鎖的一種穷娱, 但是這種鎖鎖住的卻不止一行數(shù)據(jù),他鎖住的是多行运沦,是一個數(shù)據(jù)范圍泵额。間隙鎖的主要作用是為了防止出現(xiàn)幻讀,但是它會把鎖定范圍擴大携添,
有時候也會給我們帶來麻煩嫁盲,我們就遇到了。 在數(shù)據(jù)庫參數(shù)中薪寓, 控制間隙鎖的參數(shù)是:
innodb_locks_unsafe_for_binlog
這個參數(shù)默認(rèn)值是OFF亡资, 也就是啟用間隙鎖澜共, 他是一個bool值, 當(dāng)值為true時表示disable間隙鎖锥腻。
那為了防止間隙鎖是不是直接將innodb_locaks_unsafe_for_binlog設(shè)置為true就可以了呢嗦董? 不一定!
而且這個參數(shù)會影響到主從復(fù)制及災(zāi)難恢復(fù)瘦黑, 這個方法還尚待商量京革。
五. 很容易產(chǎn)生間隙鎖的位置
間隙鎖的出現(xiàn)主要集中在同一個事務(wù)中先delete后 insert的情況下, 當(dāng)我們通過一個參數(shù)去刪除一條記錄的時候幸斥,
如果參數(shù)在數(shù)據(jù)庫中存在匹摇,那么這個時候產(chǎn)生的是普通行鎖,鎖住這個記錄甲葬, 然后刪除廊勃, 然后釋放鎖。如果這條記錄不存在经窖,
就有問題了坡垫, 數(shù)據(jù)庫會掃描索引,發(fā)現(xiàn)這個記錄不存在画侣, 這個時候的delete語句獲取到的就是一個間隙鎖冰悠,然后數(shù)據(jù)庫會向左掃描掃到第一個比給定參數(shù)小的值,向右掃描掃描到第一個比給定參數(shù)大的值配乱, 然后以此為界溉卓,構(gòu)建一個區(qū)間, 鎖住整個區(qū)間內(nèi)的數(shù)據(jù)搬泥, 一個特別容易出現(xiàn)死鎖的間隙鎖誕生了桑寨。
六 間隙鎖產(chǎn)生的原因或者說作用
間隙鎖的出現(xiàn)是為了在innodb的可重復(fù)讀隔離級別下,解決幻讀問題產(chǎn)生的佑钾。
間隙鎖會封鎖該條記錄相鄰兩個鍵之間的空白區(qū)域西疤,防止其它事務(wù)在這個區(qū)域內(nèi)插入、修改休溶、刪除數(shù)據(jù)代赁;所謂間隙是將數(shù)據(jù)分為不同區(qū)間,對該區(qū)間范圍進(jìn)行加鎖兽掰,區(qū)間的規(guī)則為左開右閉芭碍,比如當(dāng)數(shù)據(jù)為1,3,5時,對應(yīng)的區(qū)間為(-∞孽尽,1],(1,3],(3,5],(5,+∞]窖壕;
他這個行鎖+間隙鎖就組成了next—key lock,innodb在可重復(fù)度隔離級別下,采用next-key lock來防止幻讀,因此實現(xiàn)了最高的隔離級別瞻讽。
七 可能產(chǎn)生間隙鎖的位置
對于操作的數(shù)據(jù)是主鍵索引和普通索引鸳吸,有不同的加鎖規(guī)則,如下:
1)速勇、唯一索引只有鎖住多條記錄或者一條不存在的記錄的時候晌砾,才會產(chǎn)生間隙鎖,指定給某條存在的記錄加鎖的時候烦磁,只會加記錄鎖养匈,不會產(chǎn)生間隙鎖;
2)都伪、普通索引不管是鎖住單條呕乎,還是多條記錄,都會產(chǎn)生間隙鎖陨晶;
一個間隙鎖模擬
間隙鎖的分析:
表結(jié)構(gòu)如下:
CREATE TABLE `test_gap` (
`ID` int(11) NOT NULL, -- 主鍵
`NAME` varchar(255) NOT NULL, -- 非唯一索引
PRIMARY KEY (`ID`),
KEY `NAME` (`NAME`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
表中數(shù)據(jù)如下:
insert into `test_gap` values ('10','a') , ('50','a') , ('20','c'),
('60','c'),('30','e'),('70','e'),('40','g'),('80','g');
假設(shè)UPDATE NAME = 'c' 或者 DELETE NAME = 'c' 會產(chǎn)生間隙鎖:
此時會去尋找非唯一索引的間隙鎖的上下區(qū)間猬仁,對應(yīng)表里數(shù)據(jù)區(qū)間就是(a,e);
1:如果插入的數(shù)據(jù),屬于(a,e)之間珍逸,既b,d逐虚,此時無法插入,因為b,d被間隙鎖鎖定
2:如果插入的數(shù)據(jù)谆膳,不屬于(a,e)之間,那么不受間隙鎖影響撮躁,自由插入
3:如果插入的數(shù)據(jù)漱病,等于a或者e,此時需要再根據(jù)主鍵來判斷鎖定范圍:
1):如果是a,則取a的最大值ID為50把曼,只要插入的數(shù)據(jù)ID<50則可以自由插入杨帽,ID>50既全部鎖定無法插入
2):如果是e,則取e的最小值ID為30,只要插入的數(shù)據(jù)ID>30則可以自由插入嗤军,ID<30既全部鎖定無法插入
那么開始模擬死鎖情景