MySql 的锁

InnoDB 的锁

 

 

Shared and Exclusive Locks / Intention Locks

 

 

X

IX

S

IS

X

Conflict

Conflict

Conflict

Conflict

IX

Conflict

Compatible

Conflict

Compatible

S

Conflict

Conflict

Compatible

Compatible

IS

Conflict

Compatible

Compatible

Compatible

 

-------------------------------------------------------------------------------------------------------------------------------------

1,Record Lock:单个索引记录上的锁。

2,Gap Lock:间隙锁,锁定一个范围,但不包括记录本身。GAP锁的目的,是为了防止同一事务的两次当前读,出现幻读的情况。

3,Next-Key Lock:1+2,锁定一个范围,并且锁定记录本身。对于行的查询,都是采用该方法,主要目的是解决幻读的问题。

 

Record Locks

 

记录锁定是对索引记录的锁定。例如,从t WHERE c1 = 10 FOR UPDATE中选择SELECT c1;防止任何其他事务插入,更新或删除t.c1值为10的行。记录锁定始终锁定索引记录,即使没有定义索引的表也是如此。

对于这种情况,InnoDB创建一个隐藏的聚集索引(DB_ROW_ID)并将该索引用于记录锁定。

Gap Locks

 

A gap lock是对索引记录之间的间隙的锁定,或者是对第一个或最后一个索引记录之前的间隙的锁定。例如,SELECT c1 FROM t WHERE c1 BETWEEN 10 and 20 FOR UPDATE;。防止其他事务将值15插入到t.c1列中,无论该列中是否已有这样的值,因为该范围中所有现有值之间的间隙是锁定的。

gap lock 可能跨越单个索引值,多个索引值,甚至为空。

间隙锁是性能和并发性之间权衡的一部分,并且在某些事务隔离级别而非其他级别中使用。

对于使用唯一索引来锁定唯一行来锁定行的语句,不需要间隙锁定。(这不包括搜索条件仅包含多列唯一索引的某些列的情况;在这种情况下,会发生间隙锁定。)例如,如果id列具有唯一索引,则以下语句仅使用ID值为100的行的索引记录锁,其他会话是否在前面的间隙中插入行都没有关系:

SELECT * FROM child WHERE id = 100;

如果id未建立索引或具有非唯一索引,则该语句会锁定前面和后面的间隙。假如里面没有数据的情况下,会锁住(负无穷, 100),(100, 正无穷)

 

在这里还值得注意的是,可以通过不同的事务将冲突的锁保持在间隙上。例如,事务A可以在间隙上保留一个共享的间隙锁(间隙S锁),而事务B可以在同一间隙上保留排他的间隙锁(间隙X锁)。允许冲突的间隙锁的原因是,如果从索引中清除记录,则必须合并由不同事务保留在记录上的间隙锁。

 

间隙锁定可以显式禁用。如果将事务隔离级别更改为READ COMMITTED或启用innodb_locks_unsafe_for_binlog系统变量(现已弃用),则会发生这种情况。在这种情况下,对于搜索和索引扫描,间隙锁定将被禁用,并且仅用于外键约束检查和重复键检查。

 

 

Next-Key Locks

 

A next-key lock是索引记录上的Record Lock和索引记录之前的间隙上的间隙锁(gap lock)的组合锁。

InnoDB执行行级锁定的方式是,当它搜索或扫描表索引时,会在遇到的索引记录上设置共享或互斥锁。因此,行级锁实际上是索引记录锁。索引记录上的next - key lock 也会影响该索引记录之前的“间隙”。即,Next-key Locks是索引记录锁加上索引记录之前的间隙上的Gap Locks。如果一个会话在索引中的记录R上具有共享或排他锁,则另一会话不能按照索引顺序在R之前的间隙中插入新的索引记录。

 

假定索引包含值10、11、13和20。此索引的可能的Next-Key涵盖以下间隔:

(negative infinity, 10] (10, 11] (11, 13] (13, 20] (20, positive infinity)

对于最后一个间隔,next-key锁将间隙锁定在索引中的最大值上方,并且“超级”伪记录的值高于索引中的任何实际值。最高不是真正的索引记录,因此,实际上,此next-key lock仅锁定最大索引值之后的间隙。

 

开始测试:

 

1. 测试Next-Key在 主键唯一索引上面的行为。

 

首先设置自动提交,

set autocommit = 0;

 

create table t(a int primary key))engine =innodb;

 

插入数据

insert into t values(10),(11),(13),(20);

 

1.1 锁住一个不存在的值。

select * from t where a = 8 for update;

MySql 的锁

所以现在可以随便插入数据。

 

2. 测试Next-Key在 非主键唯一索引上面的行为。

 

首先设置自动提交,

set autocommit = 0;

 

drop table if exists t; create table t(a int, key id_x(a))engine =innodb;

 

插入数据

insert into t values(10),(11),(13),(20);

1.1 锁住一个不存在的值。

select * from t where a = 8 for update;

MySql 的锁

MySql 的锁

综上所述,如果采用了for update 手动加锁的话,可以防止幻读,但是如果没有加锁的话,还是会发生幻读的。并且在被指定的唯一索引列下面,会降级为行级别锁的。但是对于非唯一索引或者未找到的唯一索引列,还是会使用间隙锁。

 

-------------------------------------------------------------------------------------------------------------------------------------

默认情况下,Next-Key-Lock InnoDB在REPEATABLE READ事务隔离级别中运行,并且禁用了innodb_locks_unsafe_for_binlog系统变量。在这种情况下,InnoDB使用next-key锁定进行搜索和索引扫描,从而防止了幻像行。

Next-Key Locks 的事务数据在SHOW ENGINE INNODB STATUS和InnoDB监视器输出中看起来类似于以下内容:

RECORD LOCKS space id 58 page no 3 n bits 72 index `PRIMARY` of table `test`.`t` trx id 10080 lock_mode X Record lock, heap no 1 PHYSICAL RECORD: n_fields 1; compact format; info bits 0 0: len 8; hex 73757072656d756d; asc supremum;; Record lock, heap no 2 PHYSICAL RECORD: n_fields 3; compact format; info bits 0 0: len 4; hex 8000000a; asc ;; 1: len 6; hex 00000000274f; asc 'O;; 2: len 7; hex b60000019d0110; asc ;;

 

AUTO-INC Locks

 

AUTO-INC锁是一种特殊的表级锁,由插入到具有AUTO_INCREMENT列的表中的事务获取。在最简单的情况下,如果一个事务正在向表中插入值,那么任何其他事务都必须等待自己在该表中进行插入,以便第一个事务插入的行接收连续的主键值。