InnoDB存储引擎的行级锁

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);
INSERT INTO user(`username`,`money`) VALUES('聂哥',88); 

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=3;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0
 

 

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: 0mysql> update user set money=money+12 where id=3;
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 | 风清扬   | 20.00 |
|  3 | 聂哥     | 93.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 (HY000): Lock wait timeout exceeded; try restarting transaction
mysql>

二、显式加行锁

加锁语句:

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 | 风清扬   | 20.00 |
|  3 | 聂哥     | 98.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 | 风清扬   | 20.00 |
|  3 | 聂哥     | 98.00 |
+----+----------+-------+
3 rows in set (0.00 sec)

2.2、共享锁和意向排他锁冲突

A端

 
mysql> select * from user where id=3 lock in share mode;
+----+----------+-------+
| id | username | money |
+----+----------+-------+
|  3 | 聂哥     | 98.00 |
+----+----------+-------+
1 row in set (0.00 sec)
​
mysql> update user set money=money+5 where id=3;
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=3 for update;
+----+----------+--------+
| id | username | money  |
+----+----------+--------+
|  3 | 聂哥     | 103.00 |
+----+----------+--------+
1 row in set (0.00 sec) 

B端

mysql> select * from user where id=3 lock in share mode;
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
​
mysql> update user set money=money+5 where id=3;
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 > 2 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=3;
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

 


 

 
 
posted @ 2020-07-01 22:51  成文的博客  阅读(181)  评论(0编辑  收藏  举报