行锁

mysql 中 innodb 存储引擎支持行锁,我们平常默认使用的也是innodb存储引擎,因为innodb 引擎中有mvcc 的控制,所以我们要想测试的时候就需要手动来显示加锁

  • 共享锁  select  ....  lock  in  share mode
  • 排他锁  select  ... for update  insert  update  delete

演示

有以下表

+----+------+------+
| id | name | addr |
+----+------+------+
|  1 | xxx  | 1    |
|  4 | ccc  | 2    |
|  7 | 1    | 4    |
| 10 | fff  | 6    |
+----+------+------+

其中id 为自增主键

开启两个客户端,设置事务手动提交

                                                   

客户端1客户端2

mysql> 
mysql> 
mysql> set autocommit=0;
Query OK, 0 rows affected (0.00 sec)

mysql> 
 

mysql> 
mysql> 
mysql> set autocommit=0;
Query OK, 0 rows affected (0.00 sec)

mysql> 
 

mysql> select *from student where id = 1 lock in share mode;
+----+------+------+
| id | name | addr |
+----+------+------+
|  1 | xxx  | 1    |
+----+------+------+
1 row in set (0.00 sec)

mysql> 
 

mysql> select *from student where id = 1 lock in share mode;
+----+------+------+
| id | name | addr |
+----+------+------+
|  1 | xxx  | 1    |
+----+------+------+
1 row in set (0.00 sec)

mysql> 
 

  select *from student where id =1 for update ;

(被阻塞)

mysql> commit;
Query OK, 0 rows affected (0.01 sec)
 
mysql> select *from student where id =1 for update ;
+----+------+------+
| id | name | addr |
+----+------+------+
|  1 | xxx  | 1    |
+----+------+------+
1 row in set (0.00 sec)
 

由上表我们可以得出,共享锁可以多个客户端获取,当客户端1要获取排他锁的时候,因为客户端2事务没有提交,依然占有共享锁,所以客户端1被阻塞,直至客户端2事务提交,同样排他锁的获取也是独占的

间隙锁

+----+------+------+
| id | name | addr |
+----+------+------+
|  1 | xxx  | 1    |
|  4 | ccc  | 2    |
|  7 | 1    | 4    |
| 10 | fff  | 6    |
+----+------+------+

客户端1        客户端2

mysql> set autocommit=0;
Query OK, 0 rows affected (0.00 sec)

mysql> 

mysql> set autocommit=0;
Query OK, 0 rows affected (0.00 sec)

mysql> 

mysql> select *from student where id >20 for update ;
Empty set (0.00 sec)
 

mysql> 
mysql> insert into student values(11,'x','x');

(被阻塞)

commit;
插入成功

客户端1加排他锁来查询大于20的记录,客户端2在插入11的时候会被阻塞,这是因为,客户端1在查询的时候会对记录加间隙锁,我们当前表的间隙有 (负无穷,1),(1,4),(4,7),(7,10),(10,正无穷),当我们执行 select *from student where id >20 for update ;的时候就会对 (10,正无穷)加锁,所以客户端2会被阻塞,但是这里的10是开区间,也就是说如果客户端2对10进行修改是不会阻塞的。同样如果我们执行 select *from student where id >7 and id <10 for update ; 就会对(7,10)加间隙锁,这里的间隙锁都是没有加在记录上,如果加在了记录上就会变成临键锁,即行锁加间隙锁

临键锁

+----+------+------+
| id | name | addr |
+----+------+------+
|  1 | xxx  | 1    |
|  4 | ccc  | 2    |
|  7 | 1    | 4    |
| 10 | fff  | 6    |
+----+------+------+

客户端1        客户端2
mysql> set autocommit=0;
Query OK, 0 rows affected (0.00 sec)
mysql> set autocommit=0;
Query OK, 0 rows affected (0.00 sec)
mysql> select *from student where id >4 and id <10 for update;
+----+------+------+
| id | name | addr |
+----+------+------+
|  7 | 1    | 4    |
+----+------+------+
1 row in set (0.00 sec)
mysql> insert into student values(8,'x','x');
(被阻塞)

客户端1执行select *from student where id >4 and id <10 for update;会对(4,7】,(7,10】加锁,即 7和10 也会被锁住,即间隙(4,7),(7,10)和 行锁 7 和10;但是对4进行修改将不会阻塞

注意

上述的所有操作都是基于索引的,如果不通过索引加锁的话,锁就会升级为表锁,索引失效也会升级为表锁,其次在演示的时候所有的操作都显示的加锁,因为mvcc机制,不显示加锁的话会产生快照读

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐