innodb事務鎖
根據(jù)文檔innodb鎖分為以下幾種:
-
意向鎖:
就是簡單的IX,IS這類意向鎖,這個容易理解,比如要在表上讀取一行記錄,那么表可能會被加IS鎖,在表上寫入就會被加IX鎖
-
行鎖:
這個也非常容易理解,就是在行上面加鎖,S鎖或者X鎖
-
gap鎖:
gap鎖就是為了在repeatable read隔離級別下消除幻讀引入的一種鎖,打到serializable隔離級別的效果
-
next-key鎖:
其實這個鎖是有gap鎖和record鎖組成的,LOCK_ORDINARY 表示next-key lock 但是在8.0中已經(jīng)看不出來了。
-
插入意向鎖(INSERT_INTENTION)
插入意向鎖是gap鎖的一種,2個插入意向所gap鎖并不會產生沖突。
repeatable read 隔離級別
在這個隔離級別下,innodb為了實現(xiàn)repeatable read消除幻讀,達到serializable的效果就引入了gap lock。
next-key lock和gap lock
LOCK_ORDINARY 表示next-key lock 但是在8.0中已經(jīng)看不出來了,在performance_schema.data_locks中是gap鎖和record2條記錄:
begin;
update id = 45 where i=40;
在另外一個會話中執(zhí)行:
select thread_id,engine_transaction_id,object_name,index_name,lock_type,lock_mode,lock_status,lock_data from data_locks;
thread_id |
engine_transaction_id |
object_name |
index_name |
lock_type |
lock_mode |
lock_status |
lock_data |
240089 |
5446 |
l |
NULL |
TABLE |
IX |
GRANTED |
NULL |
240089 |
5446 |
l |
i |
RECORD |
X |
GRANTED |
40, 50 |
240089 |
5446 |
l |
PRIMARY |
RECORD |
X,REC_NOT_GAP |
GRANTED |
50 |
240089 |
5446 |
l |
i |
RECORD |
X,GAP |
GRANTED |
60, 60 |
240089 |
5446 |
l |
i |
RECORD |
X,GAP |
GRANTED |
40, 45 |
第2條和第5條組合其實就是一個next-key lock
第3條鎖定primary 50 是因為原來的id=50
插入意向鎖(INSERT_INTENTION lock)
在repeatable read隔離級別下,insert會先去判斷一下 insert intention lock若成功,插入然后釋放。且2個插入意向鎖在同一個gap中并不會堵塞測試也很簡單在上面事務的基礎上執(zhí)行語句:
insert into l values(60,35);
查看鎖信息:
thread_id |
engine_transaction_id |
object_name |
index_name |
lock_type |
lock_mode |
lock_status |
lock_data |
240289 |
5447 |
l |
NULL |
TABLE |
IX |
GRANTED |
NULL |
240289 |
5447 |
l |
PRIMARY |
RECORD |
S,REC_NOT_GAP |
GRANTED |
60 |
240289 |
5447 |
l |
i |
RECORD |
X,GAP,INSERT_INTENTION |
WAITING |
40, 45 |
240089 |
5446 |
l |
NULL |
TABLE |
IX |
GRANTED |
NULL |
240089 |
5446 |
l |
i |
RECORD |
X |
GRANTED |
40, 50 |
240089 |
5446 |
l |
PRIMARY |
RECORD |
X,REC_NOT_GAP |
GRANTED |
50 |
240089 |
5446 |
l |
i |
RECORD |
X,GAP |
GRANTED |
60, 60 |
240089 |
5446 |
l |
i |
RECORD |
X,GAP |
GRANTED |
40, 45 |
而執(zhí)行成功后鎖會變成:
thread_id |
engine_transaction_id |
object_name |
index_name |
lock_type |
lock_mode |
lock_status |
lock_data |
240289 |
5454 |
l |
NULL |
TABLE |
IX |
GRANTED |
NULL |
也就是說插入意向鎖已經(jīng)被釋放。
在2個會話中分別執(zhí)行,發(fā)現(xiàn)并不會堵塞執(zhí)行成功:
begin;
insert into l values(52,51);
begin;
insert into l values(51,50);
鎖信息:
thread_id |
engine_transaction_id |
object_name |
index_name |
lock_type |
lock_mode |
lock_status |
lock_data |
240289 |
5454 |
l |
NULL |
TABLE |
IX |
GRANTED |
NULL |
240089 |
5453 |
l |
NULL |
TABLE |
IX |
GRANTED |
NULL |
read commit 隔離級別
因為innodb的特點,mysql的update并不會堵塞select語句。除非顯示的提示加共享鎖。
在read commit隔離級別下:
begin;
update l set id = 45 where i=40;
thread_id |
engine_transaction_id |
object_name |
index_name |
lock_type |
lock_mode |
lock_status |
lock_data |
240089 |
5455 |
l |
NULL |
TABLE |
IX |
GRANTED |
NULL |
240089 |
5455 |
l |
i |
RECORD |
X,REC_NOT_GAP |
GRANTED |
40, 50 |
240089 |
5455 |
l |
PRIMARY |
RECORD |
X,REC_NOT_GAP |
GRANTED |
50 |
和之前的相比上了lock_mode 少了帶gap的標記,原先的x鎖中也加了rec_not_gap。也就是說read commit不再鎖定數(shù)據(jù)的gap。
堵塞判斷
innodb堵塞判斷因為有了gap,next-key,insert intention鎖變的復雜(lock_rec_has_to_wait函數(shù)中體現(xiàn)):
- 首選判斷意向鎖,為此還定義了一個堵塞矩陣:
static const byte lock_compatibility_matrix[5][5] = {
/** IS IX S X AI */
/* IS */ { TRUE, TRUE, TRUE, FALSE, TRUE},
/* IX */ { TRUE, TRUE, FALSE, FALSE, TRUE},
/* S */ { TRUE, FALSE, TRUE, FALSE, FALSE},
/* X */ { FALSE, FALSE, FALSE, FALSE, FALSE},
/* AI */ { TRUE, TRUE, FALSE, FALSE, FALSE} };
-
即使步驟1沖突,根據(jù)不同的lock_mode 開始判斷,以下情況不需要等待:
- 如果請求鎖住的是 supremum 或者 LOCK_GAP 為 1 并且 LOCK_INSERT_INTENTION 為 0
- 如果請求鎖 LOCK_INSERT_INTENTION 為 0 并且已有鎖是 LOCK_GAP 為 1
- 如果請求鎖 LOCK_GAP 為 1,請求鎖 LOCK_REC_NOT_GAP 為 1
- 如果已有鎖 LOCK_INSERT_INTENTION 為 1
參考:
MySQL · 引擎特性 · InnoDB 事務鎖系統(tǒng)簡介
MySQL · 引擎特性 · Innodb 鎖子系統(tǒng)淺析
|