InnoDB
經過 MVCC
和 NEXT-KEY Locks
,解決了在可重複讀
的事務隔離級別下出現幻讀
的問題。MVCC
我先挖個坑,往後再細講,這篇文章咱們主要來談談那些可愛的鎖。算法
幻讀是在可重複讀
的事務隔離級別下會出現的一種問題,簡單來講,可重複讀
保證了當前事務不會讀取到其餘事務已提交的 UPDATE
操做。但同時,也會致使當前事務沒法感知到來自其餘事務中的 INSERT
或 DELETE
操做,這就是幻讀
。sql
行鎖在 InnoDB 中是基於索引
實現的,因此一旦某個加鎖操做沒有使用索引,那麼該鎖就會退化爲表鎖
。spa
顧名思義,記錄鎖就是爲某行記錄加鎖,它封鎖該行的索引記錄
:code
-- id 列爲主鍵列或惟一索引列
SELECT * FROM table WHERE id = 1 FOR UPDATE;
複製代碼
id 爲 1 的記錄行會被鎖住。索引
須要注意的是:id
列必須爲惟一索引列
或主鍵列
,不然上述語句加的鎖就會變成臨鍵鎖
。事務
同時查詢語句必須爲精準匹配
(=
),不能爲 >
、<
、like
等,不然也會退化成臨鍵鎖
(感謝評論區 @decodes 提醒)。string
在經過 主鍵索引
與 惟一索引
對數據行進行 UPDATE 操做時,也會對該行數據加記錄鎖
:table
-- id 列爲主鍵列或惟一索引列
UPDATE SET age = 50 WHERE id = 1;
複製代碼
間隙鎖基於非惟一索引
,它鎖定一段範圍內的索引記錄
。間隙鎖基於下面將會提到的Next-Key Locking
算法,請務必牢記:使用間隙鎖鎖住的是一個區間,而不單單是這個區間中的每一條數據。class
SELECT * FROM table WHERE id BETWEN 1 AND 10 FOR UPDATE;
複製代碼
即全部在(1,10)
區間內的記錄行都會被鎖住,全部id 爲 二、三、四、五、六、七、八、9 的數據行的插入會被阻塞,可是 1 和 10 兩條記錄行並不會被鎖住。im
除了手動加鎖外,在執行完某些 SQL 後,InnoDB 也會自動加間隙鎖,這個咱們在下面會提到。
Next-Key 能夠理解爲一種特殊的間隙鎖,也能夠理解爲一種特殊的算法。經過臨建鎖能夠解決幻讀
的問題。 每一個數據行上的非惟一索引列
上都會存在一把臨鍵鎖,當某個事務持有該數據行的臨鍵鎖時,會鎖住一段左開右閉區間的數據。須要強調的一點是,InnoDB
中行級鎖
是基於索引實現的,臨鍵鎖只與非惟一索引列
有關,在惟一索引列
(包括主鍵列
)上不存在臨鍵鎖。
假設有以下表:
MySql,InnoDB,Repeatable-Read:table(id PK, age KEY, name)
id | age | name |
---|---|---|
1 | 10 | Lee |
3 | 24 | Soraka |
5 | 32 | Zed |
7 | 45 | Talon |
該表中 age
列潛在的臨鍵鎖
有:
(-∞, 10],
(10, 24],
(24, 32],
(32, 45],
(45, +∞],
在事務 A
中執行以下命令:
-- 根據非惟一索引列 UPDATE 某條記錄
UPDATE table SET name = Vladimir WHERE age = 24;
-- 或根據非惟一索引列 鎖住某條記錄
SELECT * FROM table WHERE age = 24 FOR UPDATE;
複製代碼
無論執行了上述 SQL 中的哪一句,以後若是在事務 B
中執行如下命令,則該命令會被阻塞:
INSERT INTO table VALUES(100, 26, 'Ezreal');
複製代碼
很明顯,事務 A
在對 age
爲 24 的列進行 UPDATE 操做的同時,也獲取了 (24, 32]
這個區間內的臨鍵鎖。
不只如此,在執行如下 SQL 時,也會陷入阻塞等待:
INSERT INTO table VALUES(100, 30, 'Ezreal');
複製代碼
那最終咱們就能夠得知,在根據非惟一索引
對記錄行進行 UPDATE \ FOR UPDATE \ LOCK IN SHARE MODE
操做時,InnoDB 會獲取該記錄行的 臨鍵鎖
,並同時獲取該記錄行下一個區間的間隙鎖
。
即事務 A
在執行了上述的 SQL 後,最終被鎖住的記錄區間爲 (10, 32)
。
行鎖
的實現依賴於索引
,一旦某個加鎖操做沒有使用到索引,那麼該鎖就會退化爲表鎖
。主鍵索引
在內的惟一索引
中,鎖定單條索引記錄。非惟一索引
中,鎖定開區間
範圍內的一段間隔,它是基於臨鍵鎖實現的。非惟一索引
中,該類型的每條記錄的索引上都存在這種鎖,它是一種特殊的間隙鎖,鎖定一段左開右閉
的索引區間。