[Mysql]InnoDB Locking

共享锁
排他锁
表锁
行锁
意向锁

意向锁是表锁,为了协调行锁和表锁的关系,支持多粒度(表锁与行锁)的锁并存。
当有事务A有行锁时,MySQL会自动为该表添加意向锁,事务B如果想申请整个表的写锁,那么不需要遍历每一行判断是否存在行锁,而直接判断是否存在意向锁,增强性能。

悲观锁
乐观锁

间隙锁
间隙锁只会阻塞插入操作
比如说,我有两条记录

for update

image

  • 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

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 IX S IS
X Conflict Conflict Conflict Conflict
IX Conflict Compatible Conflict Compatible
S Conflict Conflict Compatible Compatible
IS Conflict Compatible Compatible Compatible

可以对整张表加读锁或者写锁,例如,lock tables tablename write;lock tables tablename read;

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

  • “意向共享锁”(IS)表示事务打算对表中的单个行设置共享锁。
  • “意向排他锁”(IX)表示事务打算对表中的单个行设置排他锁。

例如,SELECT ... FOR SHARE 设置 IS 锁,SELECT ... FOR UPDATE 设置 IX 锁。

意向锁定协议如下:
事务在获取表中行上的共享锁之前,必须先获取表上的 IS 锁或更强的锁。
事务在获取表中行上的排他锁之前,必须先获取表上的 IX 锁。

表级锁类型兼容性总结在下表中。

总结一下,
意向锁其实是迷你版的锁,他只是指示了表里面几行数据的上的锁

  • 任意意向锁之间不会产生任何冲突

  • 表上有写锁,,那既不能对整张表读写,也不能对其中某几行读写,所以什么锁都不能再加
  • 想往表上加写锁,表上什么锁都不能有

  • 表上有意向写锁,说明表上有几行在写,肯定不能对整张表加读锁
  • 表上有意向读锁,表上有几行在读,肯定不能对整张表加写锁

  • 整张表在读,肯定不能对其中几行加写锁
  • 整张表在写,肯定不能对其中几行加读锁

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


如果请求事务与现有锁兼容,则授予该锁,但如果与现有锁冲突,则不授予该锁。事务等待,直到冲突的现有锁被释放。如果锁请求与现有锁冲突,并且由于会导致死锁而无法授予,则会发生错误。

意向锁不会阻止除全表请求(例如,LOCK TABLES ... WRITE)之外的任何操作。意向锁的主要目的是显示某人正在锁定一行或将要锁定表中的一行。

意向锁的事务数据在 SHOW ENGINE INNODB STATUS 和 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 17.6.2.1, “Clustered and Secondary Indexes”.

记录锁是“索引记录”上的锁。例如,SELECT c1 FROM t WHERE c1 = 10 FOR UPDATE; 可防止任何其他事务插入、更新或删除 t.c1 的值为 10 的行。

记录锁“始终锁定索引记录”,即使表未定义“索引”。对于此类情况,InnoDB 会创建一个“隐藏聚簇索引”并使用此索引进行记录锁定。

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        ;;

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.

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:

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.

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.

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.


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

间隙可能跨越单个索引值、多个索引值,甚至为空。

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

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

SELECT * FROM child WHERE id = 100;

如果 id 没有索引或具有非唯一索引,则该语句会锁定前面的间隙。

这里还值得注意的是,不同的事务可以在间隙上持有冲突的锁。例如,事务 A 可以在间隙上持有共享间隙锁(间隙 S 锁),而事务 B 可以在同一个间隙上持有独占间隙锁(间隙 X 锁)。允许冲突的间隙锁的原因是“如果从索引中清除了一条记录,则必须合并不同事务对该记录持有的间隙锁。”

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

可以明确禁用间隙锁定。如果将事务隔离级别更改为 READ COMMITTED,就会发生这种情况。在这种情况下,间隙锁定对于搜索和索引扫描是禁用的,并且仅用于外键约束检查和重复键检查。

使用 READ COMMITTED 隔离级别还有其他影响。在 MySQL 评估 WHERE 条件后,将释放不匹配行的记录锁。对于 UPDATE 语句,InnoDB 执行“半一致性”读取,以便它将最新提交的版本返回给 MySQL,以便 MySQL 可以确定该行是否符合 UPDATE 的 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:

(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 17.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:

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        ;;

下一个键锁是索引记录上的记录锁和索引记录前间隙上的间隙锁的组合。

InnoDB 以这样的方式执行行级锁定:当它搜索或扫描表索引时,它会在遇到的索引记录上设置共享或独占锁。因此,行级锁实际上是索引记录锁。索引记录上的下一个键锁也会影响该索引记录之前的“间隙”。也就是说,下一个键锁是索引记录锁加上索引记录前间隙上的间隙锁。 如果一个会话对索引中的记录 R 具有“共享或独占锁定”,则另一个会话无法在索引顺序中紧接在 R 之前的间隙中插入新索引记录。

假设索引包含值 10、11、13 和 20。此索引的可能下一个键锁定涵盖以下间隔,其中圆括号表示排除间隔端点,方括号表示包含端点:

(负无穷大,10]
(10,11]
(11,13]
(13,20]
(20,正无穷大)
对于最后一个间隔,下一个键锁定将间隙锁定在索引中的最大值之上,并且“supremum”伪记录的值高于索引中实际的任何值。supremum 不是真正的索引记录,因此,实际上,此下一个键锁定仅锁定最大索引值之后的间隙。

默认情况下, InnoDB 在 REPEATABLE READ 事务隔离级别下运行。在这种情况下,InnoDB 使用 next-key 锁进行搜索和索引扫描,从而防止幻像行(请参见第 17.7.4 节“幻像行”)。

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.

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:

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 |
+-----+

Client B begins a transaction to insert a record into the gap. The transaction takes an insert intention lock while it waits to obtain an exclusive lock.

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:

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  ;;...

插入意向锁是一种在插入行之前由 INSERT 操作设置的间隙锁。此锁以这样的方式发出插入意向信号:如果多个事务插入同一索引间隙的插入位置不同,则插入到该间隙的多个事务无需相互等待。假设索引记录的值为 4 和 7。尝试分别插入值 5 和 6 的独立事务在获得插入行的独占锁之前,分别使用插入意向锁锁定 4 和 7 之间的间隙,但由于行不冲突,因此不会相互阻塞。

以下示例演示了事务在获得插入记录的独占锁之前获取插入意向锁。该示例涉及两个客户端,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 |
+-----+

客户端 B 开始一个事务以将记录插入间隙。事务在等待获取排他锁时获取插入意向锁。

mysql> START TRANSACTION;
mysql> INSERT INTO child (id) VALUES (101);

插入意向锁的事务数据在 SHOW ENGINE INNODB STATUS 和 InnoDB 监视器输出中显示类似以下内容:

RECORD LOCKS 空间 id 31 页号 3 n 位 72 表 `test`.`child` 的索引 `PRIMARY`
trx id 8731 lock_mode X locks gap before rec insert intent waiting
记录锁,堆号 3 PHYSICAL RECORD:n_fields 3;紧凑格式;信息位 0
0:长度 4;十六进制 80000066asc f;;
1:长度 6;十六进制 000000002215asc " ;;
2:长度 7;十六进制 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 17.6.1.6, “AUTO_INCREMENT Handling in InnoDB”.

Predicate Locks for Spatial Indexes

InnoDB supports SPATIAL indexing of columns containing spatial data (see Section 13.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.

作者:Esofar

出处:https://www.cnblogs.com/DCFV/p/18306150

版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。

posted @   Duancf  阅读(15)  评论(0编辑  收藏  举报
more_horiz
keyboard_arrow_up light_mode palette
选择主题
点击右上角即可分享
微信分享提示