MySQL- InnoDB 中的锁详解

MySQL- InnoDB 中出现的各种锁

目录

  • MySQL中的锁分类
  • 共享锁和排他锁(Shared and Exclusive Locks)
  • 意向锁 (Intention Locks)
  • 记录锁(Record Locks)
  • 间隙锁(Gap Locks)
  • 临界锁(Next-Key Locks)
  • 插入意向锁(Insert Intention Locks)
  • 自增锁(AUTO-INC Locks)
  • Predicate Locks for Spatial Indexes
  • 总结

这篇文中主要介绍MySql中的锁的类型,文中所设计到的的知识点大部分来自于对mysql官方文档的翻译。部分内容是个人理解,大家浏览时,注意甄别文中的内容局限性。官网相关内容链接: link,本文介绍的锁都是建立在Innodb的基础上讨论的。

MySQL中的锁分类

  • 共享锁和排他锁(Shared and Exclusive Locks)
  • 意向锁(Intention Locks)
  • 记录锁 (Record Locks)
  • 间隙锁(Gap Locks)
  • 临界锁 (Next-Key Locks)
  • 插入意向锁 (Insert Intention Locks)
  • 自增长锁 (AUTO-INC Locks)
  • Predicate Locks for Spatial Indexes

个人理解中,锁的能力分为两个方向。一个方向是范围(粒度),指的是哪些数据需要被锁定。比如上面提到的意向锁,记录锁,间隙锁,临界锁。一个是锁的权限,是指授权被锁定的数据能被哪些客户端操作或者访问。比如上面提到的共享锁和排他锁。至于上面的自增长其实自带范围和权限的功能,这个接下来会有具体介绍。

共享锁和排他锁(Shared and Exclusive Locks)

原文中介绍如下:

InnoDB implements standard row-level locking where there are two types of locks, shared (S) locks and exclusive (X) locks.

  • A shared (S) lock permits the transaction that holds the lock to read a row.
  • An exclusive (X) lock permits the transaction that holds the lock to update or delete a row.

If transaction T1 holds a shared (S) lock on row r, then requests from some distinct transaction T2 for a lock on row r are handled as follows:

  • A request by T2 for an S lock can be granted immediately. As a result, both T1 and T2 hold an S lock on r.
  • A request by T2 for an X lock cannot be granted immediately.

If a transaction T1 holds an exclusive (X) lock on row r, a request from some distinct transaction T2 for a lock of either type on r cannot be granted immediately. Instead, transaction T2 has to wait for transaction T1 to release its lock on row r.

译文:

InnoDB实现了标准的行级锁,其中有两种类型的锁:共享(S)锁和独占(X)锁。
共享(S)锁 允许持有该锁的事务读取行数据。
排他(X)锁 允许持有锁的事务更新或删除行。
如果事务T1持有行 r行 上的共享(S)锁,则来自某个不同事务T2的对 r行 上锁的请求如下处理:
T2对S锁的请求可以立即被授权。此时 ,T1和T2都在r上保持S锁。
T2对X锁的请求不能立即被立即授权。
如果事务T1持有行r上的排他(X)锁,则来自某个不同事务T2的对r上任何一种类型的锁的请求不能立即被授权。相反,事务T2必须等待事务T1释放对行r的锁。

解读:

不同事务之间的共享锁客户并存,共享锁不能与排它锁共存,排它锁与排它锁之间也不能共存。上面讲的都是锁之间的权限关系。

意向锁 (Intention Locks)

原文中介绍如下:

Intention Locks
InnoDB supports multiple granularity locking which permits coexistence of row locks and table locks. For example, a statement such as LOCK TABLES … WRITE takes an exclusive lock (an X lock) on the specified table. To make locking at multiple granularity levels practical, InnoDB uses intention locks. Intention locks are table-level locks that indicate which type of lock (shared or exclusive) a transaction requires later for a row in a table. There are two types of intention locks:

  • An intention shared lock (IS) indicates that a transaction intends to set a shared lock on individual rows in a table.
  • An intention exclusive lock (IX) indicates that a transaction intends to set an exclusive lock on individual rows in a table.

For example, SELECT … FOR SHARE sets an IS lock, and SELECT … FOR UPDATE sets an IX lock.
The intention locking protocol is as follows:

  • Before a transaction can acquire a shared lock on a row in a table, it must first acquire an IS lock or stronger on the table.
  • Before a transaction can acquire an exclusive lock on a row in a table, it must first acquire an IX lock on the table.
    Table-level lock type compatibility is summarized in the following matrix.
X I X S IS
X Conflict Conflict Conflict Conflict
IX Conflict Compatible Conflict Compatible
S Conflict Conflict Compatible Compatible
IS Conflict Compatible Compatible Compatible

A lock is granted to a requesting transaction if it is compatible with existing locks, but not if it conflicts with existing locks. A transaction waits until the conflicting existing lock is released. If a lock request conflicts with an existing lock and cannot be granted because it would cause deadlock, an error occurs.
Intention locks do not block anything except full table requests (for example, LOCK TABLES … WRITE). The main purpose of intention locks is to show that someone is locking a row, or going to lock a row in the table.
Transaction data for an intention lock appears similar to the following in SHOW ENGINE INNODB STATUS and InnoDB monitor output:

TABLE LOCK table `test`.`t` trx id 10080 lock mode IX

译文:

InnoDB为了支持多粒度锁定,允许行锁和表锁共存。例如,诸如LOCK TABLES…WRITE之类的语句在指定的表上使用独占锁(X锁)。
为了实现多粒度级别的锁定,InnoDB使用意向锁。意向锁是表级锁,用于表示事务稍后对表中的行需要哪种类型的锁(共享锁或排他锁)。
有两种意图锁:

  • 意向共享锁(IS)表示事务打算在表中的单个行上设置共享锁。
  • 意向排他锁(IX)表示事务打算在表中的单个行上设置排他锁。
    例如,SELECT … FOR SHARE 设置一个IS锁,并选择…用于更新设置一个IX锁。

意图锁定协议如下:

  • 在事务可以获取表中行的共享锁之前,它必须首先获取表上的IS锁或更强的锁。
  • 在事务可以获取表中行的独占锁之前,它必须首先获取表上的IX锁。
    表级锁类型兼容性总结在以下列表中。
排他锁(X) 意向排他锁(IX) 共享锁(S) 意向共享锁(IS)
排他锁(X) 冲突 冲突 冲突 冲突
意向排他锁(IX) 冲突 共享 冲突 共享
共享锁(S) 冲突 冲突 共享 共享
意向共享锁(IS) 冲突 共享 共享 共享

如果请求事务与现有锁兼容,则授予请求事务锁。如果与现有锁冲突,事务直到冲突的现有锁被释放才会被授予锁。如果锁请求与现有锁冲突,并且由于会导致死锁而无法授予,则会发生错误。
意图锁不会阻止除完整表请求之外的任何请求(例如,LOCK TABLES … WRITE)。意图锁定的主要目的是显示有人正在锁定一行,或者要锁定表中的一行。
意向锁的事务数据在显示引擎INNODB状态和INNODB监视器输出中显示如下:

TABLE LOCK table `test`.`t` trx id 10080 lock mode IX

解读:

从上面的信息我们可以看到,意向锁分为共享意向锁和排他意向锁,他们的锁粒度都是表级锁。
意向锁存在的意义在于为了实现多粒度级别的锁定。意向锁用于表示事务稍后对表中的行需要哪种类型的锁(共享锁或排他锁)

记录锁(Record Locks)

原文中介绍如下:

A record lock is a lock on an index record. For example, SELECT c1 FROM t WHERE c1 = 10 FOR UPDATE; prevents any other transaction from inserting, updating, or deleting rows where the value of t.c1 is 10.
Record locks always lock index records, even if a table is defined with no indexes. For such cases, InnoDB creates a hidden clustered index and uses this index for record locking. See Section 15.6.2.1, “Clustered and Secondary Indexes”.
Transaction data for a record lock appears similar to the following in SHOW ENGINE INNODB STATUS and InnoDB monitor output:

RECORD LOCKS space id 58 page no 3 n bits 72 index `PRIMARY` of table `test`.`t`
trx id 10078 lock_mode X locks rec but not gap
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        ;;

译文:

记录锁是索引记录上的锁。例如,从t中选择c1,其中c1=10 用于更新;防止任何其他事务插入、更新或删除t.c1值为10的行。
记录锁始终锁定索引记录,即使定义的表没有索引。对于这种情况,InnoDB创建一个隐藏的聚集索引,并使用该索引进行记录锁定。参见第15.6.2.1节“聚集索引和二级索引”。
记录锁的事务数据在 SHOW ENGINE INNODB STATUS 输出中显示如下:

RECORD LOCKS space id 58 page no 3 n bits 72 index `PRIMARY` of table `test`.`t`
trx id 10078 lock_mode X locks rec but not gap
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        ;;

解读:

记录锁锁定的是记录中的聚簇索引,需要注意的是 c1=10满足条件的数据并不一定只有一条,所以记录锁也不是只锁定一行记录的锁。

间隙锁(Gap Locks)

原文中介绍如下:

A gap lock is a lock on a gap between index records, or a lock on the gap before the first or after the last index record. For example, SELECT c1 FROM t WHERE c1 BETWEEN 10 and 20 FOR UPDATE; prevents other transactions from inserting a value of 15 into column t.c1, whether or not there was already any such value in the column, because the gaps between all existing values in the range are locked.

译文:

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

A gap might span a single index value, multiple index values, or even be empty.
Gap locks are part of the tradeoff between performance and concurrency, and are used in some transaction isolation levels and not others.

译文:

间隙可能跨越单个索引值、多个索引值,甚至可能为空。
间隙锁是性能和并发性之间权衡的一部分,用于某些事务隔离级别,而不是其他级别。

Gap locking is not needed for statements that lock rows using a unique index to search for a unique row. (This does not include the case that the search condition includes only some columns of a multiple-column unique index; in that case, gap locking does occur.) For example, if the id column has a unique index, the following statement uses only an index-record lock for the row having id value 100 and it does not matter whether other sessions insert rows in the preceding gap:

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

SELECT * FROM child WHERE id = 100;

If id is not indexed or has a nonunique index, the statement does lock the preceding gap.
It is also worth noting here that conflicting locks can be held on a gap by different transactions. For example, transaction A can hold a shared gap lock (gap S-lock) on a gap while transaction B holds an exclusive gap lock (gap X-lock) on the same gap. The reason conflicting gap locks are allowed is that if a record is purged from an index, the gap locks held on the record by different transactions must be merged.

译文:

如果 id 列 没有索引或不具有非唯一索引,则该语句会锁定前面的间隙。
这里还值得注意的是,不同的事务可以在间隙中持有冲突锁。例如,事务A可以在某个间隙上持有共享间隙锁(gap S锁),而事务B在同一间隙上持有排他间隙锁(gap X锁)。允许冲突间隙锁的原因是,如果从索引中清除记录,则必须合并不同事务在记录上保留的间隙锁。

Gap locks in InnoDB are “purely inhibitive”, which means that their only purpose is to prevent other transactions from inserting to the gap. Gap locks can co-exist. A gap lock taken by one transaction does not prevent another transaction from taking a gap lock on the same gap. There is no difference between shared and exclusive gap locks. They do not conflict with each other, and they perform the same function.

译文:

InnoDB中的间隙锁是“purely inhibitive”,这意味着它们的唯一目的是防止其他事务插入间隙。间隙锁可以共存。一个事务使用的间隙锁不会阻止另一个事务在同一间隙上使用间隙锁。共享和独占间隙锁之间没有区别。它们彼此不冲突,并且执行相同的功能。

Gap locking can be disabled explicitly. This occurs if you change the transaction isolation level to READ COMMITTED. In this case, gap locking is disabled for searches and index scans and is used only for foreign-key constraint checking and duplicate-key checking.
There are also other effects of using the READ COMMITTED isolation level. Record locks for nonmatching rows are released after MySQL has evaluated the WHERE condition. For UPDATE statements, InnoDB does a “semi-consistent” read, such that it returns the latest committed version to MySQL so that MySQL can determine whether the row matches the WHERE condition of the UPDATE.

译文:

可以显式禁用间隙锁。如果将事务隔离级别更改为读已提交,则会发生这种情况。在这种情况下,将禁用搜索和索引扫描的间隙锁,并且仅用于外键约束检查和重复键检查。
使用读已提交隔离级别还有其他影响。在MySQL计算WHERE条件后,释放不匹配行的记录锁。对于UPDATE语句,InnoDB执行“半一致”(semi-consistent)读取,这样它将向MySQL返回最新提交的版本,以便MySQL可以确定行是否匹配更新的WHERE条件。

解读:

间隙锁作用与索引之间的范围,让人难以理解的是间隙锁之间是可以共存的(无论这不同事务之间是否是排它锁),间隙锁的唯一目的是防止其他事务插入间隙,这也就意味着,虽然大家都可以获取到排他间隙锁,但不允许插入在相同的间隙中插入数据。

临界锁(Next-Key Locks)

原文中介绍如下:

A next-key lock is a combination of a record lock on the index record and a gap lock on the gap before the index record.
InnoDB performs row-level locking in such a way that when it searches or scans a table index, it sets shared or exclusive locks on the index records it encounters. Thus, the row-level locks are actually index-record locks. A next-key lock on an index record also affects the “gap” before that index record. That is, a next-key lock is an index-record lock plus a gap lock on the gap preceding the index record. If one session has a shared or exclusive lock on record R in an index, another session cannot insert a new index record in the gap immediately before R in the index order.
Suppose that an index contains the values 10, 11, 13, and 20. The possible next-key locks for this index cover the following intervals, where a round bracket denotes exclusion of the interval endpoint and a square bracket denotes inclusion of the endpoint:

译文:

临界锁是索引记录上的记录锁和索引记录之前的间隙上的间隙锁的组合。
InnoDB以这样一种方式执行行级锁定:当搜索或扫描表索引时,它会对遇到的索引记录设置共享或排他锁。因此,行级锁实际上是索引记录锁。索引记录上的临界锁也会影响该索引记录之前的“间隙”。也就是说,临界锁是索引记录的记录锁加上索引记录前的间隙上的间隙锁。如果一个会话在索引中的记录R上具有共享或排他锁,则另一个会话不能在索引顺序中紧接R之前的间隙中插入新的索引记录。
假设索引包含值10、11、13和20。该索引可能的临界锁覆盖以下区间,其中圆括号表示排除区间端点,方括号表示包含端点:

(negative infinity 负无穷大, 10]
(10, 11]
(11, 13]
(13, 20]
(20, positive infinity 正无穷大)

For the last interval, the next-key lock locks the gap above the largest value in the index and the “supremum” pseudo-record having a value higher than any value actually in the index. The supremum is not a real index record, so, in effect, this next-key lock locks only the gap following the largest index value.
By default, InnoDB operates in REPEATABLE READ transaction isolation level. In this case, InnoDB uses next-key locks for searches and index scans, which prevents phantom rows (see Section 15.7.4, “Phantom Rows”).
Transaction data for a next-key lock appears similar to the following in SHOW ENGINE INNODB STATUS and InnoDB monitor output:

译文:

对于最后一个间隙,临界锁锁定索引中最大值和“上界限”伪记录之间的间隙,该伪记录的值高于索引中的任何实际值。上最大值不是真正的索引记录,因此,实际上,临界锁只锁定最大索引值之后的间隙。
默认情况下,InnoDB以可重复读取事务隔离级别运行。在这种情况下,InnoDB使用临界锁进行搜索和索引扫描,这防止了幻行(参见第15.7.4节“幻行”)。
下一个键锁的事务数据在显示引擎INNODB状态和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        ;;

解读:

临界锁可以看做是记录锁和间隙锁的组合,由于间隙锁阻止了其他事务的插入动作,所以也不会出现幻读的结果。(这看似是解决幻读的一个方法,至于是否可行,我并没有去验证,大家用到可以自己验证一下)

插入意向锁(Insert Intention Locks)

原文中介绍如下:

An insert intention lock is a type of gap lock set by INSERT operations prior to row insertion. This lock signals the intent to insert in such a way that multiple transactions inserting into the same index gap need not wait for each other if they are not inserting at the same position within the gap. Suppose that there are index records with values of 4 and 7. Separate transactions that attempt to insert values of 5 and 6, respectively, each lock the gap between 4 and 7 with insert intention locks prior to obtaining the exclusive lock on the inserted row, but do not block each other because the rows are nonconflicting.

译文:

插入意图锁是在行插入之前通过插入操作设置的一种间隙锁。该锁以这样一种方式发出插入意图的信号,即插入到同一索引间隙中的多个事务如果不在间隙中的同一位置插入,则不需要彼此等待。假设存在值为4和7的索引记录。分别尝试插入值为5和6的单独事务,在获得插入行的排他锁之前,每个事务都使用插入意图锁锁定4和7之间的间隙,但不会相互阻塞,因为行不冲突。

The following example demonstrates a transaction taking an insert intention lock prior to obtaining an exclusive lock on the inserted record. The example involves two clients, A and B.
Client A creates a table containing two index records (90 and 102) and then starts a transaction that places an exclusive lock on index records with an ID greater than 100. The exclusive lock includes a gap lock before record 102:

译文:

下面的示例演示了一个事务,该事务在获得插入记录的排他锁之前使用插入意图锁。该示例涉及两个客户端,A和B。
客户端A创建一个包含两个索引记录(90和102)的表,然后启动一个事务,该事务在ID大于100的索引记录上放置一个排他锁。排他锁包括记录102之前的间隙锁:

mysql> CREATE TABLE child (id int(11) NOT NULL, PRIMARY KEY(id)) ENGINE=InnoDB;
mysql> INSERT INTO child (id) values (90),(102);

mysql> START TRANSACTION;
mysql> SELECT * FROM child WHERE id > 100 FOR UPDATE;
+-----+
| id  |
+-----+
| 102 |
+-----+
mysql> START TRANSACTION;
mysql> INSERT INTO child (id) VALUES (101);

Transaction data for an insert intention lock appears similar to the following in SHOW ENGINE INNODB STATUS and InnoDB monitor output:

译文:

插入意图锁的事务数据在SHOW ENGINE INNODB STATUS 输出中显示如下:

RECORD LOCKS space id 31 page no 3 n bits 72 index `PRIMARY` of table `test`.`child`
trx id 8731 lock_mode X locks gap before rec insert intention waiting
Record lock, heap no 3 PHYSICAL RECORD: n_fields 3; compact format; info bits 0
 0: len 4; hex 80000066; asc    f;;
 1: len 6; hex 000000002215; asc     " ;;
 2: len 7; hex 9000000172011c; asc     r  ;;...

解读:

插入意图锁是在行插入之前通过插入操作设置的一种间隙锁,插入意向锁之间只要插入的不是同一行,就不会出现冲突的情况,插入意向锁在间隙锁的范围内时,插入意向锁会被阻塞。(这样做可以防止幻读)

自增锁(AUTO-INC Locks)

原文中介绍如下:

An AUTO-INC lock is a special table-level lock taken by transactions inserting into tables with AUTO_INCREMENT columns. In the simplest case, if one transaction is inserting values into the table, any other transactions must wait to do their own inserts into that table, so that rows inserted by the first transaction receive consecutive primary key values.
The innodb_autoinc_lock_mode variable controls the algorithm used for auto-increment locking. It allows you to choose how to trade off between predictable sequences of auto-increment values and maximum concurrency for insert operations.
For more information, see Section 15.6.1.6, “AUTO_INCREMENT Handling in InnoDB”.

译文:

自增锁(AUTO-INC lock)是一种特殊的表级锁,用于插入具有自增列的表的事务使用。在最简单的情况下,如果一个事务正在向表中插入值,则任何其他事务都必须等待自己向该表中插入,以便第一个事务插入的行接收连续的主键值。
innodb_autoinc_lock_mode变量控制用于自动增量锁定的算法。它允许您选择如何在可预测的自动递增值序列和插入操作的最大并发性之间进行权衡。
有关更多信息,请参阅第15.6.1.6节“InnoDB中的自动增量处理”。

解读:

在使用自增序列插入数据的时候,开启的是事务粒度是表级锁。

Predicate Locks for Spatial Indexes

InnoDB supports SPATIAL indexing of columns containing spatial data (see Section 11.4.9, “Optimizing Spatial Analysis”).
To handle locking for operations involving SPATIAL indexes, next-key locking does not work well to support REPEATABLE READ or SERIALIZABLE transaction isolation levels. There is no absolute ordering concept in multidimensional data, so it is not clear which is the “next” key.
To enable support of isolation levels for tables with SPATIAL indexes, InnoDB uses predicate locks. A SPATIAL index contains minimum bounding rectangle (MBR) values, so InnoDB enforces consistent read on the index by setting a predicate lock on the MBR value used for a query. Other transactions cannot insert or modify a row that would match the query condition.

说明:

我确实不理解这种锁,待后面遇到再更。。。。

总结

本文中的大部分知识来源于mysql官网,其中的一些解读完全是个人理解,并没有经过完全验证,大家仅做参考。欢迎各位大佬留言讨论。尽信书不如无数,大家还是具体情况具体对待吧。。。

你可能感兴趣的:(数据库,mysql,数据库,java)