MySQL InnoDB一共有四種鎖:共享鎖(讀鎖,S鎖)、排他鎖(寫鎖,X鎖)、意向共享鎖(IS鎖)和意向排他鎖(IX鎖)。其中共享鎖與排他鎖屬於行級鎖,另外兩個意向鎖屬於表級鎖。
共享鎖(讀鎖,S鎖):若事務T對數據對象A加上S鎖,則事務T可以讀A但不能修改A,其他事務只能再對A加S鎖,而不能加X鎖,直到T釋放S鎖。排他鎖(寫鎖,X鎖):若事務T對數據對象A加上X鎖,則只允許T讀取和修改A,其他事務不能再對A加作何類型的鎖,直到T釋放A上的X鎖。意向共享鎖(IS鎖):事務T在對表中數據對象加S鎖前,首先需要對該表加IS(或更強的IX)鎖。意向排他鎖(IX鎖):事務T在對表中的數據對象加X鎖前,首先需要對該表加IX鎖。
比如SELECT ... FROM T1 LOCK IN SHARE MODE語句,首先會對表T1加IS鎖,成功加上IS鎖後才會對數據加S鎖。
同樣,SELECT ... FROM T1 FOR UPDATE語句,首先會對表T1加IX鎖,成功加上IX鎖後才會對數據加X鎖。
MySQL InnoDB 鎖兼容陣列 X IX S IS X ? ? ? ? IX ? ? ? ? S ? ? ? ? IS ? ? ? ?
MySQL官網上有個死鎖的例子,但分析得過於概括,這裡我們詳細分析一下。
首先,會話S1以SELECT * FROM t WHERE i = 1 LOCK IN SHARE MODE查詢,該語句首先會對t表加IS鎖,接著會對數據(i = 1)加S鎖。
mysql> CREATE TABLE t (i INT) ENGINE = InnoDB; Query OK, 0 rows affected (1.07 sec) mysql> INSERT INTO t (i) VALUES(1); Query OK, 1 row affected (0.09 sec) mysql> START TRANSACTION; Query OK, 0 rows affected (0.00 sec) mysql> SELECT * FROM t WHERE i = 1 LOCK IN SHARE MODE; +------+ | i | +------+ | 1 | +------+ 1 row in set (0.10 sec)接著,會話S2執行DELETE FROM t WHERE i = 1,該語句嘗試對t表加IX鎖,由於IX鎖與IS鎖是兼容的,所以成功對t表加IX鎖。接著繼續對數據(i = 1)加X鎖,但數據已經被會話S1事務加了S鎖了,所以會話S2等待。
mysql> START TRANSACTION; Query OK, 0 rows affected (0.00 sec) mysql> DELETE FROM t WHERE i = 1;接著,會話S1也執行DELETE FROM t WHERE i = 1,該語句首先對t表加IX鎖,雖然會話S2已經對t表加了IX鎖,但IX鎖與IX鎖之間是兼容的,所以成功對t表加上IX鎖。接著會話S1會對數據(i = 1)加X鎖,此時發現會話S2占有的IX鎖與X鎖不兼容,所以會話S1也等待。就這樣,會話S1等S2釋放IX鎖,而會話S2等S1釋放S鎖,從而死鎖發生。
mysql> DELETE FROM t WHERE i = 1; Query OK, 1 row affected (0.00 sec) mysql>會話S2發生死鎖。
mysql> DELETE FROM t WHERE i = 1; ERROR 1213 (40001): Deadlock found when trying to get lock; try restarting transaction mysql>