【MySQL】InnoDB鎖機制之一
一 背景
MySQL鎖機制是一個極其複雜的實現,為資料庫併發訪問和資料一致提供保障。這裡僅僅針對MySQL訪問資料的三種鎖做介紹,加深自己對鎖方面的掌握。
二 常見的鎖機制
我們知道對於InnoDB儲存引擎而言,MySQL 的行鎖機制是透過在索引上加鎖來鎖定要目標資料行的。常見的有如下三種鎖型別,本文未宣告情況下都是在RR 事務隔離級別下的描述。
2.1 Record Locks
記錄鎖實際上是索引上的鎖,鎖定具體的一行或者多行記錄。當表上沒有建立索引時,InnoDB會建立一個隱含的聚族索引,並且使用該索引鎖定資料。通常我們可以使用 show innodb status 看到行鎖相關的資訊。
2.2 Gap Locks
間隙鎖是鎖定具體的範圍,但是不包含行鎖本身。比如
RR事務隔離級別下會鎖定10-20之間的記錄,不允許類似15這樣的值插入到表裡,以便消除“幻讀”帶來的影響。間隙鎖的跨度可以是1條記錄(Record low就可以認為是一個特殊的間隙鎖 ,多行,或者為空。當訪問的欄位是唯一鍵/主鍵時,間隙鎖會降級為Record lock。RR事務隔離級別下訪問一個空行 ,也會有間隙鎖,後續會舉例子說明。
我們可以透過將事務隔離級別調整為RC 模式或者設定innodb_locks_unsafe_for_binlog=1 (該引數已經廢棄)來禁用Gap鎖。
2.3 Next-Key Locks
是Record Lock+Gap Locks,鎖定一個範圍並且包含索引本身。例如索引值包含 2,4,9,14 四個值,其gap鎖的區間如下:
(-∞,2],(2,4],(4,9],(9,14],(14,+∞)
本文著重從主鍵,唯一鍵、非唯一索引,不存在值訪問四個方面來闡述RR模式下鎖的表現。
三 測試案例
3.1 主鍵/唯一鍵
會話1
會話2
分析
從例子看,當訪問表的where欄位是主鍵或者唯一鍵的時候,session2中的插入操作並未被 session1 中的id=8 影響。官方表述
需要注意以下兩種情況:
1 透過主鍵或則唯一索引訪問不存在的值,也會產生GAP鎖。
2 透過唯一索引中的一部分欄位來訪問資料,比如unique key(a,b,c) ,select * from tab where a=x and b=y; 讀者朋友可以自己做這個例子。
3.2 非唯一鍵
會話1
會話2
分析
事務1 對id=9進行for update 訪問,session2 插入[4,13]的值都是失敗的。根據MySQL的鎖原理,Innodb 範圍索引或者表是透過Next-key locks 演算法,RR事務隔離級別下,透過非唯一索引訪問資料行並不是鎖定唯一的行,而是一個範圍。從例子上可以看出來MySQL對 [4,9] 和(9,14]之間的記錄加上了鎖,防止其他事務對4-14範圍中的值進行修改。可能有讀者對其中 id=4 不能修改,但是id=14的值去可以插入有疑問?可以看接下來的例子
會話2
分析
因為session1 對非唯一鍵val=8 加上了gap鎖 [4,5] -[14,13],非此區間的記錄都可以插入表中。記錄(1,5),(15,13)不在此gap鎖區間,記錄(16,12),(16,6),(16,5)中的val值在被鎖的範圍內,故不能插入。
四 總結
寫本文的目的主要是在於溫故而知新,側重於溫故。本文著重介紹了三種鎖,其實還有兩種鎖Insert Intention Locks和AUTO-INC Locks 留作後面繼續分析。
五 推薦資料
1 官方資料
2 Innodb鎖機制:Next-Key Lock 淺談
3
MySQL鎖機制是一個極其複雜的實現,為資料庫併發訪問和資料一致提供保障。這裡僅僅針對MySQL訪問資料的三種鎖做介紹,加深自己對鎖方面的掌握。
二 常見的鎖機制
我們知道對於InnoDB儲存引擎而言,MySQL 的行鎖機制是透過在索引上加鎖來鎖定要目標資料行的。常見的有如下三種鎖型別,本文未宣告情況下都是在RR 事務隔離級別下的描述。
2.1 Record Locks
記錄鎖實際上是索引上的鎖,鎖定具體的一行或者多行記錄。當表上沒有建立索引時,InnoDB會建立一個隱含的聚族索引,並且使用該索引鎖定資料。通常我們可以使用 show innodb status 看到行鎖相關的資訊。
2.2 Gap Locks
間隙鎖是鎖定具體的範圍,但是不包含行鎖本身。比如
- select * from tab where id>10 and id<20;
我們可以透過將事務隔離級別調整為RC 模式或者設定innodb_locks_unsafe_for_binlog=1 (該引數已經廢棄)來禁用Gap鎖。
2.3 Next-Key Locks
是Record Lock+Gap Locks,鎖定一個範圍並且包含索引本身。例如索引值包含 2,4,9,14 四個值,其gap鎖的區間如下:
(-∞,2],(2,4],(4,9],(9,14],(14,+∞)
本文著重從主鍵,唯一鍵、非唯一索引,不存在值訪問四個方面來闡述RR模式下鎖的表現。
三 測試案例
3.1 主鍵/唯一鍵
-
CREATE TABLE `lck_primarkey` (
-
`id` int(11) NOT NULL,
-
val int(11) not null default 0,
-
primary key (`id`),
-
key idx_val(val)
-
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
- insert into lck_primarkey values(2,3),(4,5),(9,8),(14,13)
-
[session1] >select * from lck_primarkey;
-
+----+-----+
-
| id | val |
-
+----+-----+
-
| 2 | 3 |
-
| 4 | 5 |
-
| 9 | 8 |
-
| 14 | 13 |
-
+----+-----+
-
4 rows in set (0.00 sec)
-
[session1] >begin;
-
Query OK, 0 rows affected (0.00 sec)
-
[session1] >select * from lck_primarkey where id=9 for update;
-
+----+-----+
-
| id | val |
-
+----+-----+
-
| 9 | 8 |
-
+----+-----+
- 1 row in set (0.00 sec)
-
[session2] >begin;
-
Query OK, 0 rows affected (0.00 sec)
-
[session2] >insert into lck_primarkey values(7,6);
-
Query OK, 1 row affected (0.00 sec)
-
[session2] >insert into lck_primarkey values(5,5);
-
Query OK, 1 row affected (0.00 sec)
-
[session2] >insert into lck_primarkey values(13,13);
-
Query OK, 1 row affected (0.00 sec)
-
[session2] >insert into lck_primarkey values(10,9);
- Query OK, 1 row affected (0.00 sec)
從例子看,當訪問表的where欄位是主鍵或者唯一鍵的時候,session2中的插入操作並未被 session1 中的id=8 影響。官方表述
-
“Gap locking is not needed for statements that lock rows using a unique index to search for a unique row. (This does not include the case that the search condition includes only some columns of a multiple-column unique index; in that case, gap locking does occur.) For example, if the id column has a unique index, the following statement uses only an index-record lock for the row having id value 100 and it does not matter whether other sessions insert rows in the preceding gap:
- select * from tab where id=100 for update”
- 就是說當語句透過主鍵或者唯一鍵訪問資料的時候,Innodb會使用Record lock鎖住記錄本身,而不是使用間隙鎖鎖定範圍。
1 透過主鍵或則唯一索引訪問不存在的值,也會產生GAP鎖。
-
[session1] >begin;
-
Query OK, 0 rows affected (0.00 sec)
-
[session1] >select * from lck_primarkey where id=7 for update;
-
Empty set (0.00 sec)
-
[session2] >insert into lck_primarkey values(8,13);
-
^CCtrl-C -- sending "KILL QUERY 303042481" to server ...
-
Ctrl-C -- query aborted.
-
ERROR 1317 (70100): Query execution was interrupted
-
[session2] >insert into lck_primarkey values(5,13);
-
^CCtrl-C -- sending "KILL QUERY 303042481" to server ...
-
Ctrl-C -- query aborted.
-
ERROR 1317 (70100): Query execution was interrupted
-
[session2] >insert into lck_primarkey values(3,13);
-
Query OK, 1 row affected (0.00 sec)
-
[session2] >insert into lck_primarkey values(10,13);
- Query OK, 1 row affected (0.00 sec)
3.2 非唯一鍵
-
CREATE TABLE `lck_secondkey` (
-
`id` int(11) NOT NULL,
-
KEY `idx_id` (`id`)
-
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
- insert into lck_secondkey values(2),(4),(9),(14)
-
[session1] >begin ;
-
Query OK, 0 rows affected (0.00 sec)
-
[session1] >select * from lck_secondkey;
-
+----+
-
| id |
-
+----+
-
| 2 |
-
| 3 |
-
| 4 |
-
| 9 |
-
| 14 |
-
+----+
-
5 rows in set (0.00 sec)
-
[session1] >select * from lck_secondkey where id=9 for update;
-
+----+
-
| id |
-
+----+
-
| 9 |
-
+----+
- 1 row in set (0.00 sec)
-
[session2] >begin;
-
Query OK, 0 rows affected (0.00 sec)
-
[session2] >insert into lck_secondkey values(3);
-
Query OK, 1 row affected (0.00 sec)
-
[session2] >insert into lck_secondkey values(4);
-
^CCtrl-C -- sending "KILL QUERY 303040567" to server ...
-
Ctrl-C -- query aborted.
-
ERROR 1317 (70100): Query execution was interrupted
-
[session2] >insert into lck_secondkey values(5);
-
^CCtrl-C -- sending "KILL QUERY 303040567" to server ...
-
Ctrl-C -- query aborted.
-
ERROR 1317 (70100): Query execution was interrupted
-
[session2] >insert into lck_secondkey values(6);
-
^CCtrl-C -- sending "KILL QUERY 303040567" to server ...
-
Ctrl-C -- query aborted.
-
ERROR 1317 (70100): Query execution was interrupted
-
[session2] >insert into lck_secondkey values(7);
-
^CCtrl-C -- sending "KILL QUERY 303040567" to server ...
-
Ctrl-C -- query aborted.
-
ERROR 1317 (70100): Query execution was interrupted
-
[session2] >insert into lck_secondkey values(8);
-
^CCtrl-C -- sending "KILL QUERY 303040567" to server ...
-
Ctrl-C -- query aborted.
-
ERROR 1317 (70100): Query execution was interrupted
-
[session2] >insert into lck_secondkey values(9);
-
^CCtrl-C -- sending "KILL QUERY 303040567" to server ...
-
Ctrl-C -- query aborted.
-
ERROR 1317 (70100): Query execution was interrupted
-
[session2] >insert into lck_secondkey values(10);
-
^CCtrl-C -- sending "KILL QUERY 303040567" to server ...
-
Ctrl-C -- query aborted.
-
ERROR 1317 (70100): Query execution was interrupted
-
[session2] >insert into lck_secondkey values(11);
-
^CCtrl-C -- sending "KILL QUERY 303040567" to server ...
-
Ctrl-C -- query aborted.
-
ERROR 1317 (70100): Query execution was interrupted
-
[session2] >insert into lck_secondkey values(12);
-
^CCtrl-C -- sending "KILL QUERY 303040567" to server ...
-
Ctrl-C -- query aborted.
-
ERROR 1317 (70100): Query execution was interrupted
-
[session2] >insert into lck_secondkey values(13);
-
^CCtrl-C -- sending "KILL QUERY 303040567" to server ...
-
Ctrl-C -- query aborted.
-
ERROR 1317 (70100): Query execution was interrupted
-
[session2] >insert into lck_secondkey values(14);
- Query OK, 1 row affected (0.00 sec)
事務1 對id=9進行for update 訪問,session2 插入[4,13]的值都是失敗的。根據MySQL的鎖原理,Innodb 範圍索引或者表是透過Next-key locks 演算法,RR事務隔離級別下,透過非唯一索引訪問資料行並不是鎖定唯一的行,而是一個範圍。從例子上可以看出來MySQL對 [4,9] 和(9,14]之間的記錄加上了鎖,防止其他事務對4-14範圍中的值進行修改。可能有讀者對其中 id=4 不能修改,但是id=14的值去可以插入有疑問?可以看接下來的例子
-
[session1] >select * from lck_primarkey;
-
+----+-----+
-
| id | val |
-
+----+-----+
-
| 2 | 3 |
-
| 4 | 5 |
-
| 9 | 8 |
-
| 14 | 13 |
-
+----+-----+
-
4 rows in set (0.00 sec)
-
[session1] >begin;
-
Query OK, 0 rows affected (0.00 sec)
-
[session1] >select * from lck_primarkey where val=8 for update;
-
+----+-----+
-
| id | val |
-
+----+-----+
-
| 9 | 8 |
-
+----+-----+
- 1 row in set (0.00 sec)
-
[session2] >begin;
-
Query OK, 0 rows affected (0.00 sec)
-
[session2] >insert into lck_primarkey values(3,5);
-
Query OK, 1 row affected (0.00 sec)
-
[session2] >insert into lck_primarkey values(15,13);
-
Query OK, 1 row affected (0.00 sec)
-
[session2] >select * from lck_primarkey;
-
+----+-----+
-
| id | val |
-
+----+-----+
-
| 2 | 3 |
-
| 3 | 5 |
-
| 4 | 5 |
-
| 9 | 8 |
-
| 14 | 13 |
-
| 15 | 13 |
-
+----+-----+
- 6 rows in set (0.00 sec)
-
[session2] >insert into lck_primarkey values(16,12);
-
^CCtrl-C -- sending "KILL QUERY 303040567" to server ...
-
Ctrl-C -- query aborted.
-
ERROR 1317 (70100): Query execution was interrupted
-
[session2] >insert into lck_primarkey values(16,6);
-
^CCtrl-C -- sending "KILL QUERY 303040567" to server ...
-
Ctrl-C -- query aborted.
-
ERROR 1317 (70100): Query execution was interrupted
-
[session2] >insert into lck_primarkey values(16,5);
-
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
-
[session2] >
-
[session2] >insert into lck_primarkey values(1,5);
- Query OK, 1 row affected (0.00 sec)
因為session1 對非唯一鍵val=8 加上了gap鎖 [4,5] -[14,13],非此區間的記錄都可以插入表中。記錄(1,5),(15,13)不在此gap鎖區間,記錄(16,12),(16,6),(16,5)中的val值在被鎖的範圍內,故不能插入。
四 總結
寫本文的目的主要是在於溫故而知新,側重於溫故。本文著重介紹了三種鎖,其實還有兩種鎖Insert Intention Locks和AUTO-INC Locks 留作後面繼續分析。
五 推薦資料
1 官方資料
2 Innodb鎖機制:Next-Key Lock 淺談
3
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/22664653/viewspace-2130868/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- MySQL InnoDB 中的鎖機制MySql
- MySQL底層概述—10.InnoDB鎖機制MySql
- mysql innodb lock鎖之record lock之一MySql
- 資料庫系列:MySQL InnoDB鎖機制介紹資料庫MySql
- 全面瞭解mysql鎖機制(InnoDB)與問題排查MySql
- MySQL資料庫InnoDB儲存引擎中的鎖機制GVMySql資料庫儲存引擎
- MySQL中InnoDB鎖機制介紹及一些測試MySql
- InnoDB儲存引擎鎖機制(一、案例)儲存引擎
- InnoDB儲存引擎鎖機制(二、 鎖的型別)儲存引擎型別
- Mysql鎖機制分析MySql
- 【MySQL】MySQL中的鎖機制MySql
- InnoDB儲存引擎鎖機制(三、鎖的演算法)儲存引擎演算法
- MySQL鎖:03.InnoDB行鎖MySql
- Mysql各種鎖機制MySql
- mysql myisam的鎖機制MySql
- Mysql innodb引擎(二)鎖MySql
- MySql(三) MySql中的鎖機制MySql
- Mysql中的鎖機制——MyISAM表鎖MySql
- MySQL的事務機制和鎖(InnoDB引擎、MVCC多版本併發控制技術)MySqlMVC
- 『MySQL』搞懂 InnoDB 鎖機制 以及 高併發下如何解決超賣問題MySql
- InnoDB儲存引擎鎖機制(五、 常見問題)儲存引擎
- MySQL效能優化(九)-- 鎖機制之行鎖MySql優化
- MySQL鎖:InnoDB行鎖需要避免的坑MySql
- mysql 事務,鎖,隔離機制MySql
- mysql鎖機制 讀書筆記MySql筆記
- Mysql研磨之InnoDB行鎖模式MySql模式
- MySQL:Innodb 一個死鎖案例MySql
- 再談mysql鎖機制及原理—鎖的詮釋MySql
- mysql事務處理與鎖機制MySql
- 一小時搞懂Mysql鎖機制MySql
- MySQL學習系列之InnoDB下事務隔離機制MySql
- 一文詳解MySQL的鎖機制MySql
- MySQL中的事務原理和鎖機制MySql
- Mysql技術內幕之InnoDB鎖探究MySql
- MySQL InnoDB設定死鎖檢測的方法MySql
- 詳解 MySql InnoDB 中意向鎖的作用MySql
- MySQL探祕(四):InnoDB的磁碟檔案及落盤機制MySql
- InnoDB 崩潰恢復機制
- Mysql事務隔離級別與鎖機制MySql