mysql insert鎖機制【轉】


最近再找一些MySQL鎖表原因,整理出來一部分sql語句會鎖表的,方便查閱,整理的不是很全,都是工作中碰到的,會持續更新

筆者能力有限,如果有不正確的,或者不到位的地方,還請大家指出來,方便你我,方便大家。

測試環境

Mysql 5.5 基於innodb 引擎

 

[sql]  view plain  copy
 
  1. insert into  table1 values select  … from table2 ….  


此種方法,會鎖table2

[sql]  view plain  copy
 
  1. delete table1  from table1 inner join table2  on table1.id=table2.id  …  


此種方法,會鎖table2

[sql]  view plain  copy
 
  1. update tabel1,table2 set table1.name=’feie’ where table1.id=table2.id  


此種方法,會鎖table2

[sql]  view plain  copy
 
  1. update tabel1,table2 set table1.name=’feie’ where table1.id=table2.id and table1.id=1;  


此種方法,會鎖table2.id=1的記錄

-----------------------------------------第二次編輯分割線--------------------------------------

假設kid 是表table 的 一個索引字段 且值不唯一
1.如果kid 有多個值為12的記錄那么:
update table  set name=’feie’ where kid=12;  
會鎖表
2.如果kid有唯一的值為1的記錄那么:
update table  set name=’feie’ where kid=1;  
不會鎖
總結:用索引字段做為條件進行修改時, 是否表鎖的取決於這個索引字段能否確定記錄唯一,當索引值對應記錄不唯一,會進行鎖表,相反則行鎖。

如果有兩個delete

kid1 與 kid2是索引字段
語句1 delete from table where  kid1=1 and kid2=2;
語句2 delete from table where  kid1=1 and kid2=3;
這樣的兩個delete 是不會鎖表的
語句1 delete from table where  kid1=1 and kid2=2;
語句2 delete from table where  kid1=1 ;
這樣的兩個delete 會鎖表
總結:同一個表,如果進行刪除操作時,盡量讓刪除條件統一,否則會相互影響造成鎖表

 

轉自

Mysql 會導致鎖表的語法 - 大肥鵝的專欄 - 博客頻道 - CSDN.NET
http://blog.csdn.net/yonghumingbuzhidao/article/details/8330795

 

一、前言

上周遇到一個因insert而引發的死鎖問題,其成因比較令人費解。
於是想要了解一下insert加鎖機制,但是發現網上介紹的文章比較少且零散,挖掘過程比較忙亂。

本以為只需要系統學習一個較完全的邏輯,但是實際牽扯很多innodb鎖相關知識及加鎖方式。我好像並沒有那么大的能耐,把各種場景的加鎖過程一一列舉並加之分析;亦沒有太多的精力驗證網上的言論的准確性。

只好根據現在了解的內容,參考官方文檔,說說自己當前的理解。
本文僅供參考,如有誤導,概不負責。

二、現場狀態

不同的MySQL版本,不同的參數設置,都可能對加鎖過程有影響。
分析加鎖機制還是應當盡可能多地列舉一下關鍵參數,例如:當前mysql版本、事務隔離級別等。
如下,僅僅只列出個別比較重要的參數。

1.數據庫版本

1
2
3
4
5
6
mysql> select version();
+-----------+
| version() |
+-----------+
| 5.6.27 |
+-----------+

2. 數據庫引擎

1
2
3
4
5
6
7
8
mysql> show variables like '%engine%';
+----------------------------+--------+
| Variable_name | Value |
+----------------------------+--------+
| default_storage_engine | InnoDB |
| default_tmp_storage_engine | InnoDB |
| storage_engine | InnoDB |
+----------------------------+--------+

注:InnoDB支持事務,Myisam不支持事務;InnoDB支持行鎖和表鎖;Myisam不支持行鎖。

3. 事務隔離級別

1
2
3
4
5
6
mysql> select @@global.tx_isolation, @@session.tx_isolation, @@tx_isolation;
+-----------------------+------------------------+-----------------+
| @@global.tx_isolation | @@session.tx_isolation | @@tx_isolation |
+-----------------------+------------------------+-----------------+
| REPEATABLE-READ | REPEATABLE-READ | REPEATABLE-READ |
+-----------------------+------------------------+-----------------+

注:幾種事務隔離級別:READ UNCOMMITTED | READ COMMITTED | REPEATABLE READ | SERIALIZABLE

4. 查看gap鎖開啟狀態

1
2
3
4
5
6
mysql> show variables like 'innodb_locks_unsafe_for_binlog';
+--------------------------------+-------+
| Variable_name | Value |
+--------------------------------+-------+
| innodb_locks_unsafe_ for_binlog | OFF |
+--------------------------------+-------+

innodb_locks_unsafe_for_binlog:默認值為0,即啟用gap lock。
最主要的作用就是控制innodb是否對gap加鎖。
但是,這一設置變更並不影響外鍵和唯一索引(含主鍵)對gap進行加鎖的需要。
開啟innodb_locks_unsafe_for_binlog的REPEATABLE-READ事務隔離級別,很大程度上已經蛻變成了READ-COMMITTED。

參見官方文檔[^1]:

By default, the value of innodb_locks_unsafe_for_binlog is 0 (disabled), which means that gap locking is enabled: InnoDB uses next-key locks for searches and index scans. To enable the variable, set it to 1. This causes gap locking to be disabled: InnoDB uses only index-record locks for searches and index scans.

Enabling innodb_locks_unsafe_for_binlog does not disable the use of gap locking for foreign-key constraint checking or duplicate-key checking.

The effect of enabling innodb_locks_unsafe_for_binlog is similar to but not identical to setting the transaction isolation level to READ COMMITTED.

5. 查看自增鎖模式

1
2
3
4
5
6
mysql> show variables like 'innodb_autoinc_lock_mode';
+--------------------------+-------+
| Variable_name | Value |
+--------------------------+-------+
| innodb_autoinc_lock_mode | 1 |
+--------------------------+-------+

innodb_autoinc_lock_mode有3種配置模式:0、1、2,分別對應”傳統模式”, “連續模式”, “交錯模式”。[^8]
傳統模式:涉及auto-increment列的插入語句加的表級AUTO-INC鎖,只有插入執行結束后才會釋放鎖。這是一種兼容MySQL 5.1之前版本的策略。
連續模式:可以事先確定插入行數的語句(包括單行和多行插入),分配連續的確定的auto-increment值;對於插入行數不確定的插入語句,仍加表鎖。這種模式下,事務回滾,auto-increment值不會回滾,換句話說,自增列內容會不連續。
交錯模式:同一時刻多條SQL語句產生交錯的auto-increment值。

由於insert語句常常涉及自增列的加鎖過程,會涉及到AUTO-INC Locks加鎖過程。
為了分步了解insert加鎖過程,本文暫不討論任何涉及自增列的加鎖邏輯。
這一參數設置相關內容可能會出現在我的下一篇文章里。

n. etc

相關的參數配置越詳情越好。

三、InnoDB鎖類型[^2]

1. 基本鎖

基本鎖:共享鎖(Shared Locks:S鎖)與排他鎖(Exclusive Locks:X鎖)

mysql允許拿到S鎖的事務讀一行,允許拿到X鎖的事務更新或刪除一行。
加了S鎖的記錄,允許其他事務再加S鎖,不允許其他事務再加X鎖;
加了X鎖的記錄,不允許其他事務再加S鎖或者X鎖。

mysql對外提供加這兩種鎖的語法如下:
加S鎖:select…lock in share mode
加X鎖:select…for update

2. 意向鎖(Intention Locks)

InnoDB為了支持多粒度(表鎖與行鎖)的鎖並存,引入意向鎖。
意向鎖是表級鎖,可分為意向共享鎖(IS鎖)和意向排他鎖(IX鎖)。

InnoDB supports multiple granularity locking which permits coexistence of row-level locks and locks on entire tables. To make locking at multiple granularity levels practical, additional types of locks called intention locks are used. Intention locks are table-level locks in InnoDB that indicate which type of lock (shared or exclusive) a transaction will require later for a row in that table. There are two types of intention locks used in InnoDB (assume that transaction T has requested a lock of the indicated type on table t):
Intention shared (IS): Transaction T intends to set S locks on individual rows in table t.
Intention exclusive (IX): Transaction T intends to set X locks on those rows.

事務在請求S鎖和X鎖前,需要先獲得對應的IS、IX鎖。

Before a transaction can acquire an S lock on a row in table t, it must first acquire an IS or stronger lock on t. Before a transaction can acquire an X lock on a row, it must first acquire an IX lock on t.

意向鎖產生的主要目的是為了處理行鎖和表鎖之間的沖突,用於表明“某個事務正在某一行上持有了鎖,或者准備去持有鎖”。

The main purpose of IX and IS locks is to show that someone is locking a row, or going to lock a row in the table.

共享鎖、排他鎖與意向鎖的兼容矩陣如下:

  X IX S IS
X 沖突 沖突 沖突 沖突
IX 沖突 兼容 沖突 兼容
S 沖突 沖突 兼容 兼容
IS 沖突 兼容 兼容 兼容

思考

從官方文檔字面意思上看意向鎖是表級鎖,但是大牛不認為“Intention lock 是表級鎖”[^5]?
另外,由於意向鎖主要用於解決行鎖與表鎖間沖突問題,鑒於平時表級操作特別少,在分析加鎖過程是否可以不用過多考慮意向鎖的問題?

3. 行鎖

記錄鎖(Record Locks)

記錄鎖, 僅僅鎖住索引記錄的一行。
單條索引記錄上加鎖,record lock鎖住的永遠是索引,而非記錄本身,即使該表上沒有任何索引,那么innodb會在后台創建一個隱藏的聚集主鍵索引,那么鎖住的就是這個隱藏的聚集主鍵索引。所以說當一條sql沒有走任何索引時,那么將會在每一條聚集索引后面加X鎖,這個類似於表鎖,但原理上和表鎖應該是完全不同的。

參見官方文檔[^3]:

If the table has no PRIMARY KEY or suitable UNIQUE index, InnoDB internally generates a hidden clustered index on a synthetic column containing row ID values. The rows are ordered by the ID that InnoDB assigns to the rows in such a table. The row ID is a 6-byte field that increases monotonically as new rows are inserted. Thus, the rows ordered by the row ID are physically in insertion order.

間隙鎖(Gap Locks)

區間鎖, 僅僅鎖住一個索引區間(開區間)。
在索引記錄之間的間隙中加鎖,或者是在某一條索引記錄之前或者之后加鎖,並不包括該索引記錄本身。

next-key鎖(Next-Key Locks)

record lock + gap lock, 左開右閉區間。

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.

By default, InnoDB operates in REPEATABLE READ transaction isolation level and with the innodb_locks_unsafe_for_binlog system variable disabled. In this case, InnoDB uses next-key locks for searches and index scans, which prevents phantom rows。

默認情況下,innodb使用next-key locks來鎖定記錄。
但當查詢的索引含有唯一屬性的時候,Next-Key Lock 會進行優化,將其降級為Record Lock,即僅鎖住索引本身,不是范圍。

插入意向鎖(Insert Intention Locks)

Gap Lock中存在一種插入意向鎖(Insert Intention Lock),在insert操作時產生。在多事務同時寫入不同數據至同一索引間隙的時候,並不需要等待其他事務完成,不會發生鎖等待。
假設有一個記錄索引包含鍵值4和7,不同的事務分別插入5和6,每個事務都會產生一個加在4-7之間的插入意向鎖,獲取在插入行上的排它鎖,但是不會被互相鎖住,因為數據行並不沖突。

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.

注:插入意向鎖並非意向鎖,而是一種特殊的間隙鎖。

4. 行鎖的兼容矩陣[^4]

  Gap Insert Intention Record Next-Key
Gap 兼容 兼容 兼容 兼容
Insert Intention 沖突 兼容 兼容 沖突
Record 兼容 兼容 沖突 沖突
Next-Key 兼容 兼容 沖突 沖突

表注:橫向是已經持有的鎖,縱向是正在請求的鎖。

由於S鎖和S鎖是完全兼容的,因此在判別兼容性時只考慮持有的鎖與請求的鎖是這三種組合情形:X、S和S、X和X、X。
另外,需要提醒注意的是進行兼容判斷也只是針對於加鎖涉及的行有交集的情形。

分析兼容矩陣可以得出如下幾個結論:

  • INSERT操作之間不會有沖突。
  • GAP,Next-Key會阻止Insert。
  • GAP和Record,Next-Key不會沖突
  • Record和Record、Next-Key之間相互沖突。
  • 已有的Insert鎖不阻止任何准備加的鎖。

5. 自增鎖(AUTO-INC Locks)

AUTO-INC鎖是一種特殊的表級鎖,發生涉及AUTO_INCREMENT列的事務性插入操作時產生。

官方解釋如下[^3]:

An 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.

四、insert加鎖過程

官方文檔[^6]對於insert加鎖的描述如下:

INSERT sets an exclusive lock on the inserted row. This lock is an index-record lock, not a next-key lock (that is, there is no gap lock) and does not prevent other sessions from inserting into the gap before the inserted row.

Prior to inserting the row, a type of gap lock called an insert intention gap lock is set. 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 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.

If a duplicate-key error occurs, a shared lock on the duplicate index record is set. This use of a shared lock can result in deadlock should there be multiple sessions trying to insert the same row if another session already has an exclusive lock.

簡單的insert會在insert的行對應的索引記錄上加一個排它鎖,這是一個record lock,並沒有gap,所以並不會阻塞其他session在gap間隙里插入記錄。

不過在insert操作之前,還會加一種鎖,官方文檔稱它為insertion intention gap lock,也就是意向的gap鎖。這個意向gap鎖的作用就是預示着當多事務並發插入相同的gap空隙時,只要插入的記錄不是gap間隙中的相同位置,則無需等待其他session就可完成,這樣就使得insert操作無須加真正的gap lock。
假設有一個記錄索引包含鍵值4和7,不同的事務分別插入5和6,每個事務都會產生一個加在4-7之間的插入意向鎖,獲取在插入行上的排它鎖,但是不會被互相鎖住,因為數據行並不沖突。

假設發生了一個唯一鍵沖突錯誤,那么將會在重復的索引記錄上加讀鎖。當有多個session同時插入相同的行記錄時,如果另外一個session已經獲得該行的排它鎖,那么將會導致死鎖。

思考:Insert Intention Locks作用

Insert Intention Locks的引入,我理解是為了提高數據插入的並發能力。
如果沒有Insert Intention Locks的話,可能就需要使用Gap Locks來代替。

五、insert死鎖場景分析

接下來,帶大家看幾個與insert相關的死鎖場景。

1. duplicate key error引發的死鎖

這個場景主要發生在兩個以上的事務同時進行唯一鍵值相同的記錄插入操作。

表結構

1
2
3
4
5
6
7
8
9
CREATE TABLE `aa` (
`id` int(10) unsigned NOT NULL COMMENT '主鍵',
`name` varchar(20) NOT NULL DEFAULT '' COMMENT '姓名',
`age` int(11) NOT NULL DEFAULT '0' COMMENT '年齡',
`stage` int(11) NOT NULL DEFAULT '0' COMMENT '關卡數',
PRIMARY KEY (`id`),
UNIQUE KEY `udx_name` (`name`),
KEY `idx_stage` (`stage`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

表數據

1
2
3
4
5
6
7
8
9
10
mysql> select * from aa;
+----+------+-----+-------+
| id | name | age | stage |
+----+------+-----+-------+
| 1 | yst | 11 | 8 |
| 2 | dxj | 7 | 4 |
| 3 | lb | 13 | 7 |
| 4 | zsq | 5 | 7 |
| 5 | lxr | 13 | 4 |
+----+------+-----+-------+

事務執行時序表

T1(36727) T2(36728) T3(36729)
begin; begin; begin;
insert into aa values(6, ‘test’, 12, 3);    
  insert into aa values(6, ‘test’, 12, 3);  
    insert into aa values(6, ‘test’, 12, 3);
rollback;    
    ERROR 1213 (40001): Deadlock found when trying to get lock; try restarting transaction
  Query OK, 1 row affected (13.10 sec)  

如果T1未rollback,而是commit的話,T2和T3會報唯一鍵沖突:ERROR 1062 (23000): Duplicate entry ‘6’ for key ‘PRIMARY’

事務鎖占用情況

T1 rollback前,各事務鎖占用情況:

1
2
3
4
5
6
7
8
mysql> select * from information_schema.innodb_locks;
+--------------+-------------+-----------+-----------+-------------+------------+------------+-----------+----------+-----------+
| lock_id | lock_trx_id | lock_mode | lock_ type | lock_table | lock_index | lock_space | lock_page | lock_rec | lock_data |
+--------------+-------------+-----------+-----------+-------------+------------+------------+-----------+----------+-----------+
| 36729:24:3:7 | 36729 | S | RECORD | ` test`.`aa` | PRIMARY | 24 | 3 | 7 | 6 |
| 36727:24:3:7 | 36727 | X | RECORD | ` test`.`aa` | PRIMARY | 24 | 3 | 7 | 6 |
| 36728:24:3:7 | 36728 | S | RECORD | ` test`.`aa` | PRIMARY | 24 | 3 | 7 | 6 |
+--------------+-------------+-----------+-----------+-------------+------------+------------+-----------+----------+-----------+

 

注:mysql有自己的一套規則來決定T2與T3哪個進行回滾,本文不做討論。

死鎖日志

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
------------------------
LATEST DETECTED DEADLOCK
------------------------
2016-07-21 19:34:23 700000a3f000
*** (1) TRANSACTION:
TRANSACTION 36728, ACTIVE 199 sec inserting
mysql tables in use 1, locked 1
LOCK WAIT 4 lock struct(s), heap size 1184, 2 row lock(s)
MySQL thread id 13, OS thread handle 0x700000b0b000, query id 590 localhost root update
insert into aa values(6, 'test', 12, 3)
*** (1) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 24 page no 3 n bits 80 index `PRIMARY` of table ` test`.`aa` trx id 36728 lock_mode X insert intention waiting
Record lock, heap no 1 PHYSICAL RECORD: n_fields 1; compact format; info bits 0
0: len 8; hex 73757072656d756d; asc supremum;;
 
*** (2) TRANSACTION:
TRANSACTION 36729, ACTIVE 196 sec inserting
mysql tables in use 1, locked 1
4 lock struct(s), heap size 1184, 2 row lock(s)
MySQL thread id 14, OS thread handle 0x700000a3f000, query id 591 localhost root update
insert into aa values(6, 'test', 12, 3)
*** (2) HOLDS THE LOCK(S):
RECORD LOCKS space id 24 page no 3 n bits 80 index `PRIMARY` of table ` test`.`aa` trx id 36729 lock mode S
Record lock, heap no 1 PHYSICAL RECORD: n_fields 1; compact format; info bits 0
0: len 8; hex 73757072656d756d; asc supremum;;
 
*** (2) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 24 page no 3 n bits 80 index `PRIMARY` of table ` test`.`aa` trx id 36729 lock_mode X insert intention waiting
Record lock, heap no 1 PHYSICAL RECORD: n_fields 1; compact format; info bits 0
0: len 8; hex 73757072656d756d; asc supremum;;
 
*** WE ROLL BACK TRANSACTION (2)

死鎖成因

事務T1成功插入記錄,並獲得索引id=6上的排他記錄鎖(LOCK_X | LOCK_REC_NOT_GAP)。
緊接着事務T2、T3也開始插入記錄,請求排他插入意向鎖(LOCK_X | LOCK_GAP | LOCK_INSERT_INTENTION);但由於發生重復唯一鍵沖突,各自請求的排他記錄鎖(LOCK_X | LOCK_REC_NOT_GAP)轉成共享記錄鎖(LOCK_S | LOCK_REC_NOT_GAP)。

T1回滾釋放索引id=6上的排他記錄鎖(LOCK_X | LOCK_REC_NOT_GAP),T2和T3都要請求索引id=6上的排他記錄鎖(LOCK_X | LOCK_REC_NOT_GAP)。
由於X鎖與S鎖互斥,T2和T3都等待對方釋放S鎖。
於是,死鎖便產生了。

如果此場景下,只有兩個事務T1與T2或者T1與T3,則不會引發如上死鎖情況產生。

思考

  • 為什么發現重復主鍵沖突的時候,要將事務請求的X鎖轉成S鎖?
    (比較牽強的)個人理解,跟插入意向鎖類型,也是為了提高插入的並發效率。

  • 插入前請求插入意向鎖的作用?
    個人認為,通過兼容矩陣來分析,Insert Intention Locks是為了減少插入時的鎖沖突。

2. GAP與Insert Intention沖突引發的死鎖

表結構

1
2
3
4
5
6
CREATE TABLE `t` (
`a` int(11) NOT NULL,
`b` int(11) DEFAULT NULL,
PRIMARY KEY (`a`),
KEY `idx_b` (`b`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

表數據

1
2
3
4
5
6
7
8
9
mysql> select * from t;
+----+------+
| a | b |
+----+------+
| 1 | 2 |
| 2 | 3 |
| 3 | 4 |
| 11 | 22 |
+----+------+

事務執行時序表

T1(36831) T2(36832)
begin; begin;
select * from t where b = 6 for update;  
  select * from t where b = 8 for update;
insert into t values (4,5);  
  insert into t values (4,5);
  ERROR 1213 (40001): Deadlock found when trying to get lock; try restarting transaction
Query OK, 1 row affected (5.45 sec)  

事務鎖占用情況

T2 insert前,各事務鎖占用情況:

1
2
3
4
5
6
7
mysql> select * from information_schema.innodb_locks;
+--------------+-------------+-----------+-----------+------------+------------+------------+-----------+----------+-----------+
| lock_id | lock_trx_id | lock_mode | lock_ type | lock_table | lock_index | lock_space | lock_page | lock_rec | lock_data |
+--------------+-------------+-----------+-----------+------------+------------+------------+-----------+----------+-----------+
| 36831:25:4:5 | 36831 | X,GAP | RECORD | ` test`.`t` | idx_b | 25 | 4 | 5 | 22, 11 |
| 36832:25:4:5 | 36832 | X,GAP | RECORD | ` test`.`t` | idx_b | 25 | 4 | 5 | 22, 11 |
+--------------+-------------+-----------+-----------+------------+------------+------------+-----------+----------+-----------+

 

死鎖日志

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
------------------------
LATEST DETECTED DEADLOCK
------------------------
2016-07-28 12:28:34 700000a3f000
*** (1) TRANSACTION:
TRANSACTION 36831, ACTIVE 17 sec inserting
mysql tables in use 1, locked 1
LOCK WAIT 4 lock struct(s), heap size 1184, 3 row lock(s), undo log entries 1
MySQL thread id 38, OS thread handle 0x700000b0b000, query id 953 localhost root update
insert into t values (4,5)
*** (1) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 25 page no 4 n bits 72 index `idx_b` of table `test`.`t` trx id 36831 lock_mode X locks gap before rec insert intention waiting
Record lock, heap no 5 PHYSICAL RECORD: n_fields 2; compact format; info bits 0
0: len 4; hex 80000016; asc ;;
1: len 4; hex 8000000b; asc ;;
 
*** (2) TRANSACTION:
TRANSACTION 36832, ACTIVE 13 sec inserting
mysql tables in use 1, locked 1
3 lock struct(s), heap size 360, 2 row lock(s)
MySQL thread id 39, OS thread handle 0x700000a3f000, query id 954 localhost root update
insert into t values (4,5)
*** (2) HOLDS THE LOCK(S):
RECORD LOCKS space id 25 page no 4 n bits 72 index `idx_b` of table `test`.`t` trx id 36832 lock_mode X locks gap before rec
Record lock, heap no 5 PHYSICAL RECORD: n_fields 2; compact format; info bits 0
0: len 4; hex 80000016; asc ;;
1: len 4; hex 8000000b; asc ;;
 
*** (2) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 25 page no 3 n bits 72 index `PRIMARY` of table `test`.`t` trx id 36832 lock mode S locks rec but not gap waiting
Record lock, heap no 5 PHYSICAL RECORD: n_fields 4; compact format; info bits 0
0: len 4; hex 80000004; asc ;;
1: len 6; hex 000000008fdf; asc ;;
2: len 7; hex 8d000001d00110; asc ;;
3: len 4; hex 80000005; asc ;;
 
*** WE ROLL BACK TRANSACTION (2)

死鎖成因

事務T1執行查詢語句,在索引b=6上加排他Next-key鎖(LOCK_X | LOCK_ORDINARY),會鎖住idx_b索引范圍(4, 22)。
事務T2執行查詢語句,在索引b=8上加排他Next-key鎖(LOCK_X | LOCK_ORDINARY),會鎖住idx_b索引范圍(4, 22)。由於請求的GAP與已持有的GAP是兼容的,因此,事務T2在idx_b索引范圍(4, 22)也能加鎖成功。

事務T1執行插入語句,會先加排他Insert Intention鎖。由於請求的Insert Intention鎖與已有的GAP鎖不兼容,則事務T1等待T2釋放GAP鎖。
事務T2執行插入語句,也會等待T1釋放GAP鎖。
於是,死鎖便產生了。

注:LOCK_ORDINARY擁有LOCK_GAP一部分特性。

思考:Insert Intention鎖在加哪級索引上?

這個排他鎖加在PK上,還是二級索引上?

六、課后思考

  1. 無主鍵的加鎖過程
    無PK時,會創建一個隱式聚簇索引。加鎖在這個隱式聚簇索引會有什么不同?

  2. 復合索引加鎖過程

  3. 多條件(where condition)加鎖過程
  4. 隱式鎖與顯式鎖,隱式鎖什么情況下會轉換成顯式鎖

  5. 如果插入意向鎖不阻止任何鎖,這個鎖還有必要存在嗎?
    目前看到的作用是,通過加鎖的方式來喚醒等待線程。
    但這並不意味着,被喚醒后可以直接做插入操作了。需要再次判斷是否有鎖沖突。

七、補充知識

1. 查看事務隔離級別

SELECT @@global.tx_isolation;
SELECT @@session.tx_isolation;
SELECT @@tx_isolation;

2. 設置隔離級別

SET [SESSION | GLOBAL] TRANSACTION ISOLATION LEVEL {READ UNCOMMITTED | READ COMMITTED | REPEATABLE READ | SERIALIZABLE}
例如:set session transaction isolation level read uncommitted;

3. 查看auto_increment機制模式

show variables like ‘innodb_autoinc_lock_mode’;

4. 查看表狀態

show table status like ‘plan_branch’\G;
show table status from test like ‘plan_branch’\G;

5. 查看SQL性能

show profiles
show profile for query 1;

6. 查看當前最新事務ID

每開啟一個新事務,記錄當前最新事務的id,可用於后續死鎖分析。
show engine innodb status\G;

7. 查看事務鎖等待狀態情況

select from information_schema.innodb_locks;
select from information_schema.innodb_lock_waits;
select * from information_schema.innodb_trx;

8. 查看innodb狀態(包含最近的死鎖日志)

show engine innodb status;

八、參考文檔

[^1]: InnoDB Startup Options and System Variables
[^2]: InnoDB Locking
[^3]: Clustered and Secondary Indexes
[^4]: [MySQL] gap lock/next-key lock淺析
[^5]: Intention Lock是否表級鎖
[^6]: Locks Set by Different SQL Statements in InnoDB

[^8]: AUTO_INCREMENT lock Handing in InnoDB
[^9]: 深入理解innodb的鎖(record,gap,Next-Key lock)

[^11]: The INFORMATION_SCHEMA INNODB_LOCKS Table
[^12]: The INFORMATION_SCHEMA INNODB_TRX Table
[^13]: mysql innodb插入意向鎖

 

轉自

mysql insert鎖機制 - zhanghongzheng3213的專欄 - 博客頻道 - CSDN.NET
http://blog.csdn.net/zhanghongzheng3213/article/details/53436240

 

Insert into 加鎖機制 - and1kaney的專欄 - 博客頻道 - CSDN.NET
http://blog.csdn.net/and1kaney/article/details/51214001


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM