[精選]詳細(xì)介紹MySQL中常見(jiàn)的鎖
一、死鎖示例
考慮下面一個(gè)MySQL死鎖的示例:
有如下一張表:
CREATE TABLE `test` (`id` int(20) NOT NULL,`name` varchar(20) DEFAULT NULL,PRIMARY KEY (`id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8
表中有如下數(shù)據(jù):
mysql> SELECT * FROM test;+----+------+| id | name |+----+------+| 1 | 1 || 5 | 5 || 10 | 10 || 15 | 15 || 20 | 20 || 25 | 25 |+----+------+6 rows in set (0.00 sec)
當(dāng)數(shù)據(jù)庫(kù)的隔離級(jí)別為Repeatable Read或Serializable時(shí),我們來(lái)看這樣的兩個(gè)并發(fā)事務(wù)(場(chǎng)景一):

上面兩個(gè)并發(fā)事務(wù)一定會(huì)發(fā)生死鎖(這里之所以限定RR和Serializable兩個(gè)隔離級(jí)別,是因?yàn)橹挥羞@兩個(gè)級(jí)別下才會(huì)有間隙鎖/臨鍵鎖,而這是導(dǎo)致死鎖的根本原因,后面會(huì)詳細(xì)分析)。
我們?cè)賮?lái)看另外一個(gè)并發(fā)場(chǎng)景(場(chǎng)景二):

在這個(gè)并發(fā)場(chǎng)景下,兩個(gè)事務(wù)均能成功提交,而不會(huì)有死鎖。
二、MySQL的七種鎖
行鎖(Record Locks)
間隙鎖(Gap Locks)
臨鍵鎖(Next-key Locks)
共享鎖/排他鎖(Shared and Exclusive Locks)
意向共享鎖/意向排他鎖(Intention Shared and Exclusive Locks)
插入意向鎖(Insert Intention Locks)
自增鎖(Auto-inc Locks)
實(shí)際上,MySQL官網(wǎng)中還提到了一種預(yù)測(cè)鎖,這種鎖主要用于存儲(chǔ)了空間數(shù)據(jù)的空間索引,本文暫不討論。
1、行鎖
這MySQL的官方文檔中有以下描述:
A record lock is a lock on an index record. Record locks always lock index records, even if a table is defined with no indexes. For such cases, InnoDB creates a hidden clustered index and uses this index for record locking.
這句話(huà)說(shuō)明行鎖一定是作用在索引上的。
2、間隙鎖
在MySQL的官方文檔中有以下描述:
A gap lock is a lock on a gap between index records, or a lock on the gap before the first or after the last index record。
這句話(huà)表明間隙鎖一定是開(kāi)區(qū)間,比如(3,5)或者。在MySQL官網(wǎng)上還有一段非常關(guān)鍵的描述:
Gap locks in InnoDB are “purely inhibitive”, which means that their only purpose is to prevent other transactions from inserting to the gap. Gap locks can co-exist. A gap lock taken by one transaction does not prevent another transaction from taking a gap lock on the same gap. There is no difference between shared and exclusive gap locks. They do not conflict with each other, and they perform the same function.
在MySQL官網(wǎng)上關(guān)于間隙鎖還有一段重要描述:
Gap locking can be disabled explicitly. This occurs if you change the transaction isolation level to READ COMMITTED. Under these circumstances, gap locking is disabled for searches and index scans and is used only for foreign-key constraint checking and duplicate-key checking.
3、臨鍵鎖
在MySQL的官方文檔中有以下描述:
A next-key lock is a combination of a record lock on the index record and a gap lock on the gap before the index record.
這句話(huà)表明臨鍵鎖是行鎖+間隙鎖,即臨鍵鎖是是一個(gè)左開(kāi)右閉的區(qū)間,比如(3,5]。
在MySQL的官方文檔中還有以下重要描述:
By default, InnoDB operates in REPEATABLE READ transaction isolation level. In this case, InnoDB uses next-key locks for searches and index scans, which prevents phantom rows.
4、共享鎖/排他鎖
在MySQL的官方文檔中有以下描述:
InnoDB implements standard row-level locking where there are two types of locks, shared (S) locks and exclusive (X) locks。
A shared (S) lock permits the transaction that holds the lock to read a row.
An exclusive (X) lock permits the transaction that holds the lock to update or delete a row.
5、意向共享鎖/意向排他鎖
在MySQL的官方文檔中有以下描述:
Intention locks are table-level locks that indicate which type of lock (shared or exclusive) a transaction requires later for a row in a table。
The intention locking protocol is as follows:
Before a transaction can acquire a shared lock on a row in a table, it must first acquire an IS lock or stronger on the table.
Before a transaction can acquire an exclusive lock on a row in a table, it must first acquire an IX lock on the table.
這段話(huà)說(shuō)明意向共享鎖/意向排他鎖屬于表鎖,且取得意向共享鎖/意向排他鎖是取得共享鎖/排他鎖的前置條件。
共享鎖/排他鎖與意向共享鎖/意向排他鎖的兼容性關(guān)系:

這里需要重點(diǎn)關(guān)注的是IX鎖和IX鎖是相互兼容的,這是導(dǎo)致上面場(chǎng)景一發(fā)生死鎖的前置條件,后面會(huì)對(duì)死鎖原因進(jìn)行詳細(xì)分析。
6、插入意向鎖(IIX)
在MySQL的官方文檔中有以下重要描述:
An insert intention lock is a type of gap lock set by INSERT operations prior to row insertion. This lock signals the intent to insert in such a way that multiple transactions inserting into the same index gap need not wait for each other if they are not inserting at the same position within the gap. Suppose that there are index records with values of 4 and 7. Separate transactions that attempt to insert values of 5 and 6, respectively, each lock the gap between 4 and 7 with insert intention locks prior to obtaining the exclusive lock on the inserted row, but do not block each other because the rows are nonconflicting.
這段話(huà)表明盡管插入意向鎖是一種特殊的間隙鎖,但不同于間隙鎖的是,該鎖只用于并發(fā)插入操作。如果說(shuō)間隙鎖鎖住的是一個(gè)區(qū)間,那么插入意向鎖鎖住的就是一個(gè)點(diǎn)。因而從這個(gè)角度來(lái)說(shuō),插入意向鎖確實(shí)是一種特殊的間隙鎖。與間隙鎖的另一個(gè)非常重要的差別是:盡管插入意向鎖也屬于間隙鎖,但兩個(gè)事務(wù)卻不能在同一時(shí)間內(nèi)一個(gè)擁有間隙鎖,另一個(gè)擁有該間隙區(qū)間內(nèi)的插入意向鎖(當(dāng)然,插入意向鎖如果不在間隙鎖區(qū)間內(nèi)則是可以的)。這里我們?cè)倩仡櫼幌鹿蚕礞i和排他鎖:共享鎖用于讀取操作,而排他鎖是用于更新或刪除操作。也就是說(shuō)插入意向鎖、共享鎖和排他鎖涵蓋了常用的增刪改查四個(gè)動(dòng)作。
7、示例分析
到此為止,我們介紹了MySQL常用的七種鎖的前六種,理解了這六種鎖之后,才能很好地分析和理解開(kāi)頭給出的兩個(gè)場(chǎng)景。我們先來(lái)分析場(chǎng)景一:

在場(chǎng)景一中,因?yàn)镮X鎖是表鎖且IX鎖之間是兼容的,因而事務(wù)一和事務(wù)二都能同時(shí)獲取到IX鎖和間隙鎖。另外,需要說(shuō)明的是,因?yàn)槲覀兊母綦x級(jí)別是RR,且在請(qǐng)求X鎖的時(shí)候,查詢(xún)的對(duì)應(yīng)記錄都不存在,因而返回的都是間隙鎖。接著事務(wù)一請(qǐng)求插入意向鎖,這時(shí)發(fā)現(xiàn)事務(wù)二已經(jīng)獲取了一個(gè)區(qū)間間隙鎖,而且事務(wù)一請(qǐng)求的插入點(diǎn)在事務(wù)二的間隙鎖區(qū)間內(nèi),因而只能等待事務(wù)二釋放間隙鎖。這個(gè)時(shí)候事務(wù)二也請(qǐng)求插入意向鎖,該插入點(diǎn)同樣位于事務(wù)一已經(jīng)獲取的間隙鎖的區(qū)間內(nèi),因而也不能獲取成功,不過(guò)這個(gè)時(shí)候,MySQL已經(jīng)檢查到了死鎖,于是事務(wù)二被回滾,事務(wù)一提交成功。
分析并理解了場(chǎng)景一,那場(chǎng)景二理解起來(lái)就會(huì)簡(jiǎn)單多了:

場(chǎng)景二中,兩個(gè)間隙鎖沒(méi)有交集,而各自獲取的插入意向鎖也不是同一個(gè)點(diǎn),因而都能執(zhí)行成功。
往期精選

MySQL 三萬(wàn)字精華總結(jié) —鎖機(jī)制和性能調(diào)優(yōu)
MySQL 三萬(wàn)字精華總結(jié) + 面試100 問(wèn)
