MySQL的InnoDB Locking(锁)

版权声明:原创文章,转载请注明出处! https://blog.csdn.net/L_15156024189/article/details/87298122

Shared and Exclusive Locks

(1)shared locks是共享锁,简称S锁,exclusive locks是排它锁,简称X锁,它们既可以是表级锁,也可以是行级锁,在MySQL的InnoDB引擎中是行级锁,可以加在一行或者多行上,那么何时在一行上加锁,何时在多行上加锁,这需要根据索引情况而定,后面会说明。平时说的读锁指的就是S锁,写锁指的就是X锁。

(2)shared locks允许持有某行S锁的事务读取(select)该行,exclusive locks允许持有某行X锁的事务更新(update)和删除(delete)该行。

(3)如果事务T1获得了行r的S锁,另一个事务T2可以获取行r的S锁,但是不能获取行r的X锁。也就是说S锁可以被多个事务共享,所以称为共享锁。

(4)如果事务T1获得了行r的S锁,另一个事务T2既不能获取行r的S锁,也不能获取行r的X锁,必须等待T1释放S锁。所以称为排他锁。

第(3)和(4)条可以使用下面的表说明:

  X S
X 不兼容 不兼容
S 不兼容 兼容

 下面使用一个例子来说明S锁和X锁的兼容性:

(1)分别打开两个会话(会话A和会话B),在会话A中开启一个事务并执行

 select * from userinfo where name='Mike' lock in share mode;

此时该事务会给name='Mike'的记录添加S锁,然后在会话B中开启另一个事务,并分别执行

select * from userinfo where name='Mike' lock in share mode;
select * from userinfo where name='Mike' for update;

 此时第一个sql可以正常执行,而第二个sql会等待。如图:

 (2)重新打开两个会话(会话A和会话B),在会话A中开启一个事务并执行

select * from userinfo where name='Mike' for update;

 此时该事务会给name='Mike'的记录添加X锁,然后在会话B中开启另一个事务,并分别执行

select * from userinfo where name='Mike' lock in share mode;
select * from userinfo where name='Mike' for update;

  此时两个sql都会等待,如图:

Intention Locks

        Intention Locks称为意向锁,它是表级锁,顾名思义,它是用来锁定表的,与行级锁相对应。如果事务T1获取了一个表的intention exclusive锁(简称IX锁),相当于表级别的排它锁,那么事务T2就不能再获取表上的S和X锁了;如果事务T1获取了一个表的intention shared锁(简称IS锁),那么事务T2可以获取表的S锁,但不能获取表的X锁;它与共享锁和排它锁的关系如下:

(1)一个事务获取一张表中某行的S锁之前,必须获取表的IS锁或者更强的锁(比如IX);

(2)一个事务获取一张表中某行的X锁之前,必须获取表的IX锁;

表级锁的兼容性如下:

  X IX S IS
X Conflict Conflict Conflict Conflict
IX Conflict Compatible Conflict Compatible
S Conflict Conflict Compatible Compatible
IS Conflict Compatible Compatible Compatible

表级锁S和IS的区别:

表级锁S表示事务要读取表的所有行,IS表示事务打算读取表中的某些行,当然也可以是所有行。

表级锁X和IX的区别:

表级锁X表示事务要修改表的所有行,IX表示事务打算修改表中的某些行,当然也可以是所有行。

理解了这两个区别就容易理解表级锁的兼容性了,比如IX和IX怎么会兼容?原因是当前事务想要修改某张表的一些行,那么首先要获取该表的IX锁,然后在要修改的行上加上X锁,另一个事务也准备要修改该表的一些行,因为表中除了被当前事务加锁的其他行还是可以修改的,所以可以获取该表的IX锁,然后在其他行添加X锁,但是如果要修改当前事务加锁的行就需要等待了。

可以使用如下SQL语句,分别创建表级锁S和X:

LOCK TABLE my_tabl_name READ;  

LOCK TABLE my_table_name WRITE;

Record Locks

Record Locks称为记录锁,它通常加在索引记录上。下面通过例子来理解。userinfo表中的字段、数据和索引如下:

userinfo表有3个字段(id、name、age),2条记录,其中name列上创建了index_name索引。现在分别打开两个会话(会话A和会话B),先在会话A中,开启一个事务,并执行如下查询:

SELECT name FROM userinfo WHERE name='Mike' FOR UPDATE;

此时会在该条记录上加上x锁(也就是记录锁),然后在会话B中开启另一个事务,分别执行更新、删除和插入操作,具体如图:

从图中可以看到,会话B中的事务

(1)无法对当前记录进行更新和删除操作,但可以对其他记录进行更新和删除操作;

(3)无法进行插入操作;

上面给出的是查询的where子句中name字段上有索引,如果没有索引情况会怎样呢?现在删除name上的索引,然后在会话A对某条记录执行更新操作,最后在会话B中无法更新、删除和插入任何数据,原因是会话A执行更新操作时给所有的记录都加了x锁。

        实际上,如果name上有索引index_name,那么就使用index_name索引锁定该记录;如果name上没有索引,那么就使用主键索引锁定记录,此时会锁定表中全部记录;如果表中没有定义任何索引,那么 InnoDB会创建隐藏的聚集索引,然后使用聚集索引来锁定记录。

Gap Locks

Gap Locks称为间隙锁。直接看例子理解,userinfo表数据如下:

age上的index_age索引如下:

分别打开两个会话(会话A和会话B),先在会话A中开启事务,并执行如下SQL:

select * from userinfo where age between 20 and 30 for update;

然后在会话B中开启另一个事务,尝试插入一些数据,如图:

有些数据可以插入,有些数据不可以插入。插入条件区间是[20,30],会在表中记录中找到最接近20和30的两个记录值,分别是:

15和35,以15为左区间,35为右区间组成的区间[15,35)中的值都不能插入,区间外的值可以插入。也就是会话A中事务锁定了记录和记录之间的间隙(表中不存在的记录,比如age=25的记录)。这就是间隙锁。

        间隙锁主要是为了防止其他事务插入数据,但不能防止其他事务更新数据,间隙锁可以共存,而且shared and exclusive gap locks没有任何区别,它们不会冲突。以上是在RR隔离级别下测试的额,在RC隔离级别中,间隙锁失效。

上面是以age上有普通索引的情况为例的,

(1)如果age是唯一索引,且执行

select * from userinfo where age=20 for update;

这种情况不会产生间隙锁,但是如果where子句中有多个唯一索引,仍然会产生间隙锁;

Next-Key Locks

Next-Key Locks是记录锁和间隙锁两者的结合,InnoDB默认隔离级别是RR,默认使用Next-Key Locks锁。

Insert Intention Locks

AUTO-INC Locks

Predicate Locks for Spatial Indexes

实践

查看行锁争用状态

SQL如下:

SHOW STATUS LIKE '%innodb_row_lock%'

结果如图:

(1)Innodb_row_lock_current_waits

当前正在争夺行锁等待的个数,结果显示为0,表示当前没有出现行锁争夺等待。

(2)Innodb_row_lock_time

所有争夺行锁等待的总时间,单位ms;

(3)Innodb_row_lock_time_avg

每个行锁争夺等待的平均时间,每次行锁争夺会重新计算:Innodb_row_lock_time_avg=Innodb_row_lock_time/Innodb_row_lock_waits;比如查询结果中

1273642/38约等于33516.89,舍弃小数部分

(4)Innodb_row_lock_time_max

行锁争夺等待的最大时间,单位ms;

(5)Innodb_row_lock_waits

总共出现行锁争夺等待的次数,出现一次行锁争夺等待,自动加1。

争夺锁争夺的等待时间innodb_lock_wait_timeout决定,默认是50s,可以如下查询:

SHOW VARIABLES like '%innodb_lock_wait_timeout%'

结果:】

查看InnoDB状态,SQL如下:

SHOW ENGINE INNODB STATUS;

结果如图:

Status包含了很多内容,这里只关注TRANSACTIONS,内容如下:

------------
TRANSACTIONS
------------
Trx id counter 68867
Purge done for trx's n:o < 0 undo n:o < 0 state: running but idle
History list length 0
LIST OF TRANSACTIONS FOR EACH SESSION:
---TRANSACTION 281765184739120, not started
0 lock struct(s), heap size 1136, 0 row lock(s)

猜你喜欢

转载自blog.csdn.net/L_15156024189/article/details/87298122