数据库锁与加锁情况介绍(记录锁、间隙锁、next-key锁)

数据库的锁

关于mysql有哪些锁?锁的分类有哪些?可查看另外一篇博客:mysql 的锁

下面主要是记录在实际情景中的加锁情况进行分析

测试环境介绍

innodb 引擎下,隔离级别在 REPEATABLE READ 进行测试

表结构介绍

(1)简单的测试表一:study表,id为主键

create table study(

id int  primary key,

name varchar(255),

age int

) engine=innodb;
insert study values(7 , 'g' , 21);

(2)简单的测试表二:study表,id为主键

create table my_table(
id int  primary key,
year int
) engine=innodb;
insert into my_table values(1, 2000),(2,2005),(3,2007),(4,2010),(5,2012),(6,2017);

隔离级别的介绍

1、数据库的隔离级别:

能”表示能解决,“否”表示不能解决。

脏读不可重复读幻读
未提交读
提交读
可重复读
串行读

2、查询数据库的隔离级别

SELECT @@tx_isolation

3、修改数据库隔离级别:

SET [GLOBAL|SESSION] TRANSACTION ISOLATION LEVEL {READ UNCOMMITTED | READ COMMITTED | REPEATABLE READ| SERIALIZABLE}

  • 隔离级别名称可选值:READ UNCOMMITTED,READ COMMITTED,REPEATABLE READ,SERIALIZABLE
  • global:设置全局session,但当前session不受影响。
  • session,设置当前和之后的所有session。
  • 都不写:设置当前session下一个还未开始的事务。

4、本测试环境使用的隔离级别

请添加图片描述

加锁情况介绍

具体查看这篇博客,这里大概还是列举一下锁有哪些:

mysql 的锁

注意:MyISAM引擎,如果需要进行锁表的操作,必须用lock table来进行,delete、update,insert语句会自动锁整个表

下面是对innodb引擎的锁类型进行介绍:

1、锁分类介绍:

(1)从锁的范围来分

  • 行锁:记录锁、间隙锁、next-key 锁
  • 表锁

(2)从对资源访问是否加锁来分:

  • 乐观锁:不对资源加锁,只有在更新的时候才会判断数据会不会被修改,如果被修改,则修改失败
  • 悲观锁:访问的时候就对资源加锁,加锁后别的访问不允许对其进行更新/删除操作

在这里插入图片描述

(3)从对数据的访问形式来分

  • 读锁:共享读
  • 写锁:独享写

2、加锁具体的实现方式

(1)默认的加锁及其对应加锁的类型

  • 普通的select(没有加lock in share mode或for update)不会锁表或锁行的
  • 常见的增删改(INSERT、DELETE、UPDATE)语句会自动对操作的数据行加写锁,其他对该数据进行操作的时候需要等待该事务提交后

(2)显式的加锁

  • select xxxxx lock in share mode :加的是共享读锁,不会影响其他事务的读(这里指不加锁的读,可以再次加共享读锁,读上加写锁会失败),但会影响其他事务的写
  • select xxxx for update : 加的是写锁,不加锁的读不会被影响,任意加锁的读会受影响,其他事务的写也会受影响

(3)举例:(在study表上进行测试)

举例一:测试共享读(lock in share mode)

事务一:

开启一个事务,对 id 为 7的加一个共享读锁(lock in share mode),不提交事务

在这里插入图片描述

事务二:

第一次尝试:开启一个事务,对 id 为 7的进行更新(会尝试获取到锁),操作不成功

在这里插入图片描述

第二次尝试:开启一个事务,也对 id 为 7的进行查询(不加锁的查询),操作成功

在这里插入图片描述

第三次尝试:开启一个事务,也对 id 为 7的进行查询(加共享读锁的查询),操作成功

在这里插入图片描述

第四次尝试:开启一个事务,也对 id 为 7的进行查询(加独享写锁的查询),操作失败

在这里插入图片描述

举例二:测试写锁(for update)

事务一:

开启一个事务,对 id 为 7的加一个写锁(for update),不提交事务

在这里插入图片描述

事务二:

第一次尝试:开启一个事务,对 id 为 7的进行更新(会尝试获取到锁),操作不成功

在这里插入图片描述

第二次尝试:开启一个事务,也对 id 为 7的进行查询(不加锁的查询),操作成功

在这里插入图片描述

第三次尝试:开启一个事务,也对 id 为 7的进行查询(加共享读锁的查询),操作失败(加写锁的读同理,也不会操作成功)

在这里插入图片描述

不同索引下的加锁问题

判断加不同的行锁时候加锁的类型是啥

(1)主键索引

上面举例介绍得到的一些总结,当id为主键的时候 对study 表的一些sql的加锁情况:

1. select * from study where id = 7; # 快照读,对其他的读和写没有影响
2. select * from study where id = 7 lock in share mode; # 加共享读锁,不影响其他的加共享读,但是影响加任意形式的写锁
3. select * from study where id = 7 for update; # 加写锁,影响加任意形式的读或者写锁

(2)非唯一索引

基于my_table这个表进行测试,目前的数据大小范围是**[2000 , 2017]**

在这里插入图片描述

第一个事务:

在year字段上加上一个普通的索引,然后进行between查询

create index idx on my_table(year);
start transaction;
select * from my_table where year between 2007 and 2010 for update;

第二个事务:

start transaction;
insert into my_table values(10 , 2004); =>Query OK,
delete from my_table where year=2005; =>Query OK,
insert into my_table values(11 ,2005); =>ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
insert into my_table values(11, 2006); =>ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
insert into my_table values(11, 2007); =>ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
insert into my_table values(11, 2008); =>ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
insert into my_table values(11, 2010); =>ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
insert into my_table values(11, 2011); =>ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
delete from my_table where year=2012; => ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
insert into my_table values(11, 2012); =>Query OK,

在这里插入图片描述

加锁分析:

(1)加锁范围:(2005,2012], 也就是在2007,2010,2012上各加了一个Next key lock(记录锁+间隙锁)

在这里插入图片描述

(3)非唯一索引,唯一查询

事务一:

开启事务,查询2010这条记录,目前这个记录是唯一的一条数据

start transaction;
select * from my_table where year =2010 for update;

事务一:

delete from my_table where year=2007 => Query OK
insert into my_table values(12 ,2007); =>ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
insert into my_table values(12 , 2009); =>ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
mysql> insert into my_table values(12 ,2010); =>ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
mysql> insert into my_table values(12 ,2011); =>ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
mysql> delete from my_table where year=2012; => Query OK

加锁分析:

(1)加锁范围:(2007,2012), 在2010上加了一记录 锁,在2007-2010,2010-2012上加了一个间隙锁

在这里插入图片描述

(4)唯一索引,唯一查询

创建唯一索引

drop index idx on my_table;
create unique index idx on my_table(year);

事务一:

开启事务,查询2010这条记录,目前这个记录是唯一的一条数据,year也是唯一索引

start transaction;
select * from my_table where year =2010 for update;

事务二

insert into my_table values(13, 2009); =>Query OK
insert into my_table values(14, 2010); =>ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
insert into my_table values(15 ,2011); =>Query OK

加锁分析:只在2010上加了一个行锁(record锁)

(5)唯一索引,不唯一查询

事务一:

开启事务,between查询

start transaction;
select * from my_table where year between 2007 and 2010 for update;

事务二:

delete from my_table where year=2005; => Query OK
insert into my_table values(15, 2006); =>ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
insert into my_table values(15, 2007); =>ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
insert into my_table values(15, 2008); =>ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
insert into my_table values(15, 2010); =>ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
insert into my_table values(15, 2011); =>ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
insert into my_table values(15, 2012); =>ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
delete from my_table where year=2012; =>ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
insert into my_table values(15,2013); =>Query OK

加锁分析:

(1)加锁范围:(2005,2012], 也就是在2007,2010,2012上各加了一个Next key lock(记录锁+间隙锁),与非唯一索引between查询情况一致

(6)总结

a、只有字段是唯一索引且只有一条记录的情况下,不用加Gap锁,其他情况都要加Gap锁。

b、间隙Gap锁锁定的区域:根据检索条件向左寻找最靠近检索条件的记录值A,作为左区间,向右寻找最靠近检索条件的记录值B作为右区间,即锁定的间隙为(A,B)。

c、如果不加索引进行加锁的扫描,那就是全表扫描,直接就是加上表锁。不涉及到更细粒度的行锁了

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值