InnoDB存儲引擎的行級鎖

前言

InnoDB存儲引擎和MyISAM的其中有兩個很重要的區別:一個是事務,一個就是鎖機制不同。事務之前有介紹,有問題的去補課;鎖方面的不同是InnoDB引擎既有表鎖又有行鎖,表鎖的應用和MyISAM表鎖用法一樣,行鎖只有通過有索引的字段作爲條件檢索的時候,纔會使用行級鎖,反之則是表鎖。

一、隱式加鎖

創建表和測試數據

用戶表user中id爲主鍵索引,username爲普通索引,money字段爲普通字段。在非事務環境下,隱式加鎖的過程時間非常短,不便研究。下面的栗子都是在事務環境下

CREATE TABLE `user`(
id INT PRIMARY KEY AUTO_INCREMENT,
`username` VARCHAR(50),
`money` DECIMAL(7,2),
key username(username)
)ENGINE=innodb CHARSET=utf8;

INSERT INTO user(`username`,`money`) VALUES('喬峯',5);
INSERT INTO user(`username`,`money`) VALUES('風清揚',20);

1.行鎖

當我們對用戶表進行增刪改(insert、delete、update)的時候,如果有檢索條件中使用到了索引,MySQL服務器會自動給當前操作的表添加行鎖。

終端A
更新id爲3的記錄,id爲主鍵索引,所以會引發行級鎖。

mysql> begin;
Query OK, 0 rows affected (0.00 sec)

mysql> update user set money=money+5 where id=2;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0

終端B
B端在執行update操作的時候,先是光標不停閃爍,且進入鎖等待狀態,後報鎖超時錯誤

mysql> update user set money=money+5 where id=1;
Query OK, 1 row affected (0.08 sec)
Rows matched: 1  Changed: 1  Warnings: 0

mysql> update user set money=money+12 where id=2;
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction

終端A

A端執行提交(commit),系統會自動釋放表鎖

mysql> commit;
Query OK, 0 rows affected (0.03 sec)

2.表鎖

如果有檢索條件(money字段)中沒有使用到了索引,MySQL服務器會自動給當前操作的表添加表鎖。

終端A

mysql> select * from user;
+----+----------+-------+
| id | username | money |
+----+----------+-------+
|  1 | 喬峯     | 10.00 |
|  2 | 風清揚   | 37.00 |
+----+----------+-------+
3 rows in set (0.02 sec)
mysql> begin;
Query OK, 0 rows affected (0.00 sec)

mysql> update user set money=money+5 where money=93.00;
Query OK, 1 row affected (0.01 sec)
Rows matched: 1  Changed: 1  Warnings: 0

終端B

mysql> begin;
Query OK, 0 rows affected (0.00 sec)

mysql> update user set money=money+12 where id=2;
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
mysql> update user set money=money+12 where id=1;
ERROR 1205 

二、顯式加行鎖

加鎖語句:

SELECT 查詢語句 LOCK IN SHARE MODE | FOR UPDATE 

我們只需在正常的查詢語句後面加LOCK IN SHARE MODE | FOR UPDATE就能實現添加行級讀鎖和行級排他鎖

1.意向鎖

意向鎖是InnoDB自動加的,不需用戶干預,對於UPDATE、DELETE、INSERT操作,InnoDB會自動給操作的數據加排他鎖。 我們在顯式加行鎖的時候,MySQL服務器會自動給操作表添加一個意向鎖。此意向鎖是隱式添加的,多個意向鎖之間不會產生衝突且相互兼容

2.鎖的兼容問題

- 共享鎖 排他鎖 意向共享鎖 意向排他鎖
共享鎖 兼容 衝突 兼容 衝突
排他鎖 衝突 衝突 衝突 衝突
2.1.共享鎖兼容

終端A

mysql> begin;
Query OK, 0 rows affected (0.00 sec)

mysql> select * from user lock in share mode;
+----+----------+-------+
| id | username | money |
+----+----------+-------+
|  1 | 喬峯     | 10.00 |
|  2 | 風清揚   | 37.00 |
+----+----------+-------+
3 rows in set (0.00 sec)

終端B

mysql> begin;
Query OK, 0 rows affected (0.00 sec)

mysql> select * from user lock in share mode;
+----+----------+-------+
| id | username | money |
+----+----------+-------+
|  1 | 喬峯     | 10.00 |
|  2 | 風清揚   | 37.00 |
+----+----------+-------+
3 rows in set (0.00 sec)
2.2.共享鎖和意向排他鎖衝突

終端A

mysql> select * from user where id=2 lock in share mode;
+----+----------+-------+
| id | username | money |
+----+----------+-------+
|  2 | 風清揚    | 20.00 |
+----+----------+-------+
1 row in set (0.00 sec)

mysql> update user set money=money+5 where id=2;
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
2.3.排他鎖與其他鎖都衝突

終端A

mysql> begin;
Query OK, 0 rows affected (0.00 sec)

mysql> select * from user where id=2 for update;
+----+----------+--------+
| id | username | money  |
+----+----------+--------+
|  3 | 風清揚    | 37.00  |
+----+----------+--------+
1 row in set (0.00 sec)

終端B

mysql> select * from user where id=2 lock in share mode;
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction

mysql> update user set money=money+5 where id=2;
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction

三、間隙鎖

間隙鎖是針對一個範圍條件的檢索時,InnoDB會給符合條件的已有的數據記錄的索引項加鎖;對於鍵值在條件範圍內但是並不存在的記錄,叫做“間隙”,InnoDB也會對這個“間隙”加鎖,這種鎖機制就是間隙鎖
終端A

mysql> begin;
Query OK, 0 rows affected (0.00 sec)

mysql> select * from user where id > 1 for update;
+----+----------+--------+
| id | username | money  |
+----+----------+--------+
|  3 | 風清揚    | 103.00 |
+----+----------+--------+
1 row in set (0.00 sec)

終端B

mysql> update user set money=money+5 where id=2;
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction

mysql> INSERT INTO user(`username`,`money`) VALUES('jack',99);
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章