鎖
作為一個(gè)程序員我相信鎖的概念對(duì)你來說一定不會(huì)很陌生,在開發(fā)的過程中都或多或少的接觸過,我們都知道鎖的種類一般分為樂觀鎖和悲觀鎖兩種,不管是悲觀鎖還是樂觀鎖都是為了解決并發(fā)問題的.
樂觀鎖
樂觀鎖的實(shí)現(xiàn)用的是一種沖突檢驗(yàn)的思想,當(dāng)線程對(duì)一條數(shù)據(jù)進(jìn)行寫操作時(shí),先判斷他是否被修改過,如果沒有那么可以執(zhí)行寫操作,否則丟棄該操作或重試操作,在整個(gè)的執(zhí)行過程中其實(shí)都沒有對(duì)數(shù)據(jù)進(jìn)行加鎖;
悲觀鎖
悲觀鎖的實(shí)現(xiàn)是在沖突未產(chǎn)生之前事先對(duì)資源進(jìn)行加鎖,確保同一時(shí)刻只有有限的線程能夠訪問該資源,其他想要嘗試獲取資源的操作都會(huì)進(jìn)入等待狀態(tài),直到該線程完成了對(duì)資源的操作并且釋放了鎖后,其他線程才能重新操作資源;
樂觀鎖和悲觀鎖的使用
當(dāng)我們使用樂觀鎖的時(shí)候不用考慮死鎖的問題,但是由于樂觀鎖的實(shí)現(xiàn)基于沖突檢驗(yàn),所以當(dāng)沖突頻率和重試成本較高時(shí)更推薦使用悲觀鎖,而需要非常高的響應(yīng)速度并且并發(fā)量非常大的時(shí)候使用樂觀鎖就能較好的解決問題,在這時(shí)使用悲觀鎖就可能出現(xiàn)嚴(yán)重的性能問題;在選擇并發(fā)控制機(jī)制時(shí),需要綜合考慮上面的四個(gè)方面(沖突頻率、重試成本、響應(yīng)速度和并發(fā)量)進(jìn)行選擇。
鎖的種類
我們都知道對(duì)于數(shù)據(jù)的操作無非是讀和寫兩種,所以innodb在實(shí)現(xiàn)鎖的時(shí)候?qū)@兩種操作使用不同的鎖,他們分別是共享鎖(Shared Lock)和互斥鎖(Exclusive Lock);
共享鎖(讀鎖):允許事務(wù)對(duì)一條行數(shù)據(jù)進(jìn)行讀??;
互斥鎖(寫鎖):允許事務(wù)對(duì)一條行數(shù)據(jù)進(jìn)行刪除或更新;
讀默認(rèn)共享,寫默認(rèn)排他.

鎖的粒度
innodb支持表鎖和行鎖兩種不同粒度的鎖.
行鎖
innodb實(shí)現(xiàn)了標(biāo)準(zhǔn)的行鎖,也就是行共享鎖(Shared Lock)和行互斥鎖(Exclusive Lock)
行共享鎖:當(dāng)innodb搜索表索引時(shí),它會(huì)在遇到的索引記錄上設(shè)置共享鎖.
行排他鎖:當(dāng)innodb掃描表索引時(shí),它會(huì)在遇到的索引記錄上設(shè)置排他鎖.
無論是共享鎖還是互斥鎖其實(shí)都只是對(duì)某一個(gè)數(shù)據(jù)行進(jìn)行加鎖;為了支持多粒度鎖定,InnoDB 存儲(chǔ)引擎引入了意向鎖(Intention Lock),意向鎖就是一種表級(jí)鎖。
表鎖
與行鎖的種類相似的是,意向鎖也分為兩種:
意向共享鎖:事務(wù)想要在獲得表中某些記錄的共享鎖,需要在表上先加意向共享鎖;
意向互斥鎖:事務(wù)想要在獲得表中某些記錄的互斥鎖,需要在表上先加意向互斥鎖;
隨著意向鎖的加入,鎖類型之間的兼容矩陣也變得愈加復(fù)雜:

意向鎖的意義
意向鎖其實(shí)不會(huì)阻塞全表掃描之外的任何請(qǐng)求,它們的主要目的是為了標(biāo)識(shí)表中的某一行數(shù)據(jù)是否已經(jīng)被請(qǐng)求鎖定了。
看到這里可能你會(huì)有一些疑惑?為什么意向鎖是這樣設(shè)計(jì)的?
你可以設(shè)想一下這樣的情景
如果沒有意向鎖,當(dāng)已經(jīng)有人使用行鎖對(duì)表中的某一行進(jìn)行修改時(shí),如果另外一個(gè)請(qǐng)求要對(duì)全表進(jìn)行修改,那么就需要對(duì)所有的行是否被鎖定進(jìn)行掃描,在這種情況下,效率是非常低的;不過,在引入意向鎖之后,當(dāng)有人使用行鎖對(duì)表中的某一行進(jìn)行修改之前,會(huì)先為表添加意向互斥鎖(IX),再為行記錄添加互斥鎖(X),在這時(shí)如果有人嘗試對(duì)全表進(jìn)行修改就不需要判斷表中的每一行數(shù)據(jù)是否被加鎖了,只需要通過等待意向互斥鎖被釋放就可以了。
鎖的實(shí)現(xiàn)
前面簡(jiǎn)單的介紹了一些鎖的概念,了解了在對(duì)數(shù)據(jù)庫(kù)進(jìn)行讀寫時(shí)會(huì)獲取不同的鎖,接下來我將介紹innodb是如何將鎖添加到對(duì)應(yīng)的數(shù)據(jù)行上的,我們會(huì)分別介紹幾種鎖的算法:Record Lock、Gap Lock 和 Next-Key Lock等。官方文檔
Record Lock(記錄鎖)
記錄鎖是對(duì)索引記錄的鎖定,剛剛介紹過innodb在搜索或掃描表索引時(shí),它會(huì)在遇到的索引記錄上設(shè)置共享鎖或排他鎖。因此,記錄鎖可以理解為行鎖的實(shí)現(xiàn).
記錄鎖例子描述
CREATE TABLE users (id INT NOT NULL AUTO_INCREMENT, last_name VARCHAR(255) NOT NULL, first_name VARCHAR(255), age INT , PRIMARYKEY(id) , KEY(last_name) , KEY(age))engine=innodb;
當(dāng)我們使用 索引鍵(id?或者?last_name )作為 SQL 中?WHERE?語句的過濾條件時(shí),InnoDB 會(huì)通過索引建立的 B+ 樹找到行記錄并添加記錄鎖,但是如果使用?first_name?作為過濾條件時(shí),由于 InnoDB 不知道待操作的記錄具體存放的位置,也無法對(duì)將要操作哪條記錄提前做出判斷就會(huì)鎖定整個(gè)表。
Gap Lock(間隙鎖)
間隙鎖是對(duì)索引記錄中的一段連續(xù)區(qū)域的鎖定;InnoDB實(shí)現(xiàn)間隙鎖是用來防止其他事務(wù)對(duì)間隙進(jìn)行操作的。間隙鎖是可以共存的。意思是當(dāng)一個(gè)事務(wù)占用的間隙鎖時(shí)不會(huì)阻止另一個(gè)事務(wù)在同一個(gè)間隙上進(jìn)行間隙鎖定。共享和排他間隙鎖之間沒有區(qū)別。它們彼此不沖突,它們執(zhí)行相同的功能。
間隙鎖例子描述
當(dāng)使用類似?SELECT * FROM users WHERE id BETWEEN 10 AND 20 FOR UPDATE;的 SQL 語句時(shí),innodb就會(huì)在[10,20]之間加上間隙鎖用來阻止其他事務(wù)對(duì) 10<=id<=20 記錄的操作,間隙可以跨越單個(gè)索引值,多個(gè)索引值,甚至可能為空。
Next-Key Lock
Next-Key 鎖相比前兩者就稍微有一些復(fù)雜,是索引記錄上的記錄鎖和索引記錄之前的間隙上的間隙鎖的組合。
Next-Key Lock例子描述
假設(shè)索引包含值10,11,13和20.此索引的可能的下一個(gè)鍵鎖定包括以下間隔,其中圓括號(hào)表示排除間隔端點(diǎn),方括號(hào)表示包含端點(diǎn):
(negative infinity, 10]
(10, 11]
(11, 13]
(13, 20]
(20, positive infinity)
默認(rèn)情況下,InnoDB以?REPEATABLE READ事務(wù)隔離級(jí)別運(yùn)行。在這種情況下,InnoDB使用下一鍵鎖進(jìn)行搜索和索引掃描,這會(huì)阻止幻影行(請(qǐng)參見第14.7.4節(jié)“幻影行”)。
插入意向鎖
插入意向鎖是一種專門針對(duì)insert操作的鎖,假設(shè)在插入前,該間隙已經(jīng)有間隙鎖,那么Insert會(huì)申請(qǐng)插入意向鎖。插入意向鎖是這樣設(shè)計(jì)的:如果插入到相同索引間隙中的多個(gè)事務(wù)不插入間隙內(nèi)的相同位置,則不需要等待彼此。假設(shè)存在值為4和7的索引記錄。兩個(gè)事物分別嘗試插入值5和6,在獲取插入行上的排它鎖之前,每個(gè)事物都可以對(duì)4和7之間的間隙加插入意向鎖,并且不要互相阻塞因?yàn)樾惺欠菦_突的。
插入意向鎖例子描述
客戶端A創(chuàng)建一個(gè)包含兩個(gè)索引記錄(90和102)的表,然后啟動(dòng)一個(gè)事務(wù),該事務(wù)對(duì)ID大于100的索引記錄放置排他鎖。排他鎖包括記錄102之前的間隙鎖:
mysql> CREATE TABLE child (id int(11) NOT NULL, PRIMARY KEY(id)) ENGINE=InnoDB;
mysql> INSERT INTO child (id) values (90),(102);mysql> START TRANSACTION;
mysql> SELECT * FROM child WHERE id > 100 FOR UPDATE;
|+-----+|
? ? id
|+-----+|
? ? 102
|+-----+|
客戶端B開始事務(wù)以將記錄插入間隙。該事務(wù)在等待獲取獨(dú)占鎖時(shí)采用插入意向鎖。
mysql>STARTTRANSACTION;
mysql>INSERTINTOchild(id)VALUES(101);
AUTO-INC Locks
AUTO-INC鎖是當(dāng)向使用含有AUTO_INCREMENT列的表中插入數(shù)據(jù)時(shí)需要獲取的一種特殊的表級(jí)鎖
在最簡(jiǎn)單的情況下,如果一個(gè)事務(wù)正在向表中插入值,則任何其他事務(wù)必須等待對(duì)該表執(zhí)行自己的插入操作,以便第一個(gè)事務(wù)插入的行的值是連續(xù)的。
innodb_autoinc_lock_mode配置選項(xiàng)控制用于自動(dòng)增量鎖定的算法。 它允許您選擇如何在可預(yù)測(cè)的自動(dòng)遞增值序列和插入操作的最大并發(fā)性之間進(jìn)行權(quán)衡。
有關(guān)更多信息,請(qǐng)參見?第14.6.1.4節(jié)“InnoDB中的AUTO_INCREMENT處理”。
謂詞鎖
InnoDB支持SPATIAL?對(duì)包含空間列的列進(jìn)行索引(請(qǐng)參見?第11.5.8節(jié)“優(yōu)化空間分析”)。
要處理涉及SPATIAL索引的操作的鎖定?,Next-Key Lock不能很好地支持REPEATABLE READ或?SERIALIZABLE事務(wù)隔離級(jí)別。多維數(shù)據(jù)中沒有絕對(duì)排序概念,因此不清楚哪個(gè)是?“?下一個(gè)”密鑰。
要為具有SPATIAL索引的表啟用隔離級(jí)別?,請(qǐng)InnoDB?使用謂詞鎖。
聲明:文章圖片以及相關(guān)資料來自https://draveness.me/mysql-innodb.html
如有侵犯您的知識(shí)產(chǎn)權(quán)和版權(quán)問題,請(qǐng)通知本人,本人會(huì)即時(shí)做出處理刪除文章。
如果本博客的文章在知識(shí)點(diǎn)上有錯(cuò)誤,歡迎指出錯(cuò)誤所在,歡迎多多交流。謝謝!
參考資料:https://wx2.qq.com/cgi-bin/mmwebwx-bin/webwxcheckurl?requrl=https%3A%2F%2Fdev.mysql.com%2Fdoc%2Frefman%2F5.7%2Fen%2Finnodb-locking.html&skey=%40crypt_f43a8450_c582350c5419370ab04199879640e46e&deviceid=e041497283477858&pass_ticket=undefined&opcode=2&scene=1&username=@4aec4a8ea53f992132048ffa91dbdf31aa74fc618da5fb4fd03902c20da5a7b4