大概幾個月以前項目中用到事務,須要保證數據的強一致性,期間也用到了mysql的鎖,但當時對mysql的鎖機制只是管中窺豹,因此本文打算總結一下mysql的鎖機制。mysql
本文主要論述關於mysql鎖機制,mysql版本爲5.7,引擎爲innodb,因爲實際中關於innodb鎖相關的知識及加鎖方式不少,因此沒有那麼多精力羅列全部場景下的加鎖過程並加以分析,僅根據如今瞭解的知識,結合官方文檔,說說本身的理解,若是發現有不對的地方,歡迎指正。sql
總的來講,InnoDB共有七種類型的鎖:數據庫
使用的語義爲:併發
能夠看到,一旦寫數據的任務沒有完成,數據是不能被其餘任務讀取的,這對併發度有較大的影響。對應到數據庫,能夠理解爲,寫事務沒有提交,讀相關數據的select也會被阻塞,這裏的select是指加了鎖的,普通的select仍然能夠讀到數據(快照讀)。優化
InnoDB爲了支持多粒度鎖機制(multiple granularity locking),即容許行級鎖與表級鎖共存,而引入了意向鎖(intention locks)。意向鎖是指,將來的某個時刻,事務可能要加共享/排它鎖了,先提早聲明一個意向。spa
加鎖的語法爲:code
select ... lock in share mode; 要設置IS鎖; select ... for update; 要設置IX鎖;
事務要得到某些行的S/X鎖,必須先得到表對應的IS/IX鎖,意向鎖僅僅代表意向,意向鎖之間相互兼容,兼容互斥表以下:blog
IS | IX | |
IS | 兼 容 | 兼 容 |
IX | 兼 容 | 兼 容 |
雖然意向鎖之間互相兼容,可是它與共享鎖/排它鎖互斥,其兼容互斥表以下:索引
S | X | |
IS | 兼 容 | 互 斥 |
IX | 互 斥 | 互 斥 |
排它鎖是很強的鎖,不與其餘類型的鎖兼容。這其實很好理解,修改和刪除某一行的時候,必須得到強鎖,禁止這一行上的其餘併發,以保障數據的一致性。事務
記錄鎖,它封鎖索引記錄,例如(其中id爲pk):
create table lock_example(id smallint(10),name varchar(20),primary key id)engine=innodb;
數據庫隔離級別爲RR,表中有以下數據:
10, zhangsan
20, lisi 30, wangwu
select * from t where id=1 for update;
其實這裏是先獲取該表的意向排他鎖(IX),再獲取這行記錄的排他鎖(個人理解是由於這裏直接命中索引了),以阻止其餘事務插入,更新,刪除id=1的這一行。
間隙鎖,它封鎖索引記錄中的間隔,或者第一條索引記錄以前的範圍,又或者最後一條索引記錄以後的範圍。依然是上面的例子,InnoDB,RR:
select * from lock_example where id between 8 and 15 for update;
這個SQL語句會封鎖區間(8,15),以阻止其餘事務插入id位於該區間的記錄。
間隙鎖的主要目的,就是爲了防止其餘事務在間隔中插入數據,以致使「不可重複讀」。若是把事務的隔離級別降級爲讀提交(Read Committed, RC),間隙鎖則會自動失效。
臨鍵鎖,是記錄鎖與間隙鎖的組合,它的封鎖範圍,既包含索引記錄,又包含索引區間。
默認狀況下,innodb使用next-key locks來鎖定記錄。但當查詢的索引含有惟一屬性的時候,Next-Key Lock 會進行優化,將其降級爲Record Lock,即僅鎖住索引自己,不是範圍。
舉個例子,依然是如上的表lock_example,可是id降級爲普通索引(key),也就是說即便這裏聲明瞭要加鎖(for update),並且命中的是索引,可是由於索引在這裏沒有UK約束,因此innodb會使用next-key locks,數據庫隔離級別RR:
事務A執行以下語句,未提交:
select * from lock_example where id = 20 for update;
事務B開始,執行以下語句,會阻塞:
insert into lock_example values('zhang',15);
如上的例子,事務A執行查詢語句以後,默認給id=20這條記錄加上了next-key lock,因此事務B插入10(包括)到30(不包括)之間的記錄都會阻塞。臨鍵鎖的主要目的,也是爲了不幻讀(Phantom Read)。若是把事務的隔離級別降級爲RC,臨鍵鎖則也會失效。
對已有數據行的修改與刪除,必須增強互斥鎖(X鎖),那麼對於數據的插入,是否還須要加這麼強的鎖,來實施互斥呢?插入意向鎖,孕育而生。
插入意向鎖,是間隙鎖(Gap Locks)的一種(因此,也是實施在索引上的),它是專門針對insert操做的。多個事務,在同一個索引,同一個範圍區間插入記錄時,若是插入的位置不衝突,不會阻塞彼此。
Insert Intention 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.
舉個例子(表依然是如上的例子lock_example,數據依然是如上),事務A先執行,在10與20兩條記錄中插入了一行,還未提交:
insert into t values(11, xxx);
事務B後執行,也在10與20兩條記錄中插入了一行:
insert into t values(12, ooo);
由於是插入操做,雖然是插入同一個區間,可是插入的記錄並不衝突,因此使用的是插入意向鎖,此處A事務並不會阻塞B事務。
自增鎖是一種特殊的表級別鎖(table-level lock),專門針對事務插入AUTO_INCREMENT類型的列。最簡單的狀況,若是一個事務正在往表中插入記錄,全部其餘事務的插入必須等待,以便第一個事務插入的行,是連續的主鍵值。
AUTO-INC lock is a special table-level lock taken by transactions inserting into tables with AUTO_INCREMENT columns. In the simplest case, if one transaction is inserting values into the table, any other transactions must wait to do their own inserts into that table, so that rows inserted by the first transaction receive consecutive primary key values.
舉個例子(表依然是如上的例子lock_example),可是id爲AUTO_INCREMENT,數據庫表中數據爲:
1, zhangsan 2, lisi 3, wangwu
事務A先執行,還未提交: insert into t(name) values(xxx);
事務B後執行: insert into t(name) values(ooo);
此時事務B插入操做會阻塞,直到事務A提交。
以上總結的7種鎖,我的理解能夠按兩種方式來區分:
1. 按鎖的互斥程度來劃分,能夠分爲共享、排他鎖;
2. 按鎖的粒度來劃分,能夠分爲:
其中