14.7.1 InnoDB Locking

本节主要讲述 InnoDB 引擎使用的锁类型

  • 共享锁和排他锁
  • 意向锁
  • 行锁
  • 间隙锁
  • Next-Key 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)锁

  • 一个共享锁允许持有锁的事务读取一行
  • 一个排他锁允许持有锁的事务更新或删除一行

如果事务 T1 持有第 r 行数据的共享锁, 那么对于来自某些不同事务 T2,对行 r 的锁请求将按以下方式处理:

  • 若 T2 请求获取共享锁,那么将会立即得到。结果是 T1 和 T2 都持有行 r 上的共享锁。
  • 若 T2 请求获取排他锁,那么将不会立即得到。

如果事务 T1 持有行 r 上的排他锁,那么来自其他事务 T2 请求获取共享锁或者排他锁,都不能立即获得。T2 必须等到 T1 释放持有的排他锁,才能获取得到锁。

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 … LOCK IN SHARE MODE 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.

xIXSIS
X冲突冲突冲突冲突
IX冲突兼容冲突兼容
S冲突冲突兼容兼容
IS冲突兼容兼容兼容

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 表示获取指定表上的一个排他锁。为了实现在多粒度级别实现锁,InnoDB 使用意向锁。意向锁是一个表级别的锁, 指示事务稍后对表中的行需要哪种类型的锁(共享锁或者排他锁)。有两种类型的意向锁:

例如,SELECT … LOCK IN SHARE MODE设置一个 IS 锁,SELECT … FOR UPDATE 设置一个 IX 锁。意向锁协议如下:

  • 一个事务能够获取表中一行数据的共享锁的前提是,它必须先获取这个表的一个 IS 锁或更强的锁。
  • 一个事务能够获取表中一行数据的排他锁的前提是,它必须先获取这个表的一个 IX 锁。

表级锁不同类型之间的兼容如下:

xIXSIS
X冲突冲突冲突冲突
IX冲突兼容冲突兼容
S冲突冲突兼容兼容
IS冲突兼容兼容兼容

事务请求获取锁,如果与存在的锁兼容,那么将能够获取到。如果与存在的锁冲突,则获取不到。如果冲突事务将等待,直到现有的锁被释放。如果一个锁请求和存在的锁冲突从而无法获取锁,那么可能会导致死锁,发生错误。

意向锁除了全表请求(例如, LOCK TABLES … WRITE)外,不阻塞其他任何事情。意向锁的主要目的是表明有人正在锁定表中的一行,或者打算锁定表中的行。

意向锁的事务数据格式类似于 SHOW ENGINE INNODB STATUSInnoDB monitor 的输出

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

行锁

行锁是直接加在索引记录上的。例如,SELECT c1 FROM t WHERE c1 = 10 FOR UPDATE; 阻止其他任何事务插入、更新或者删除 t.c1 值是 10 的行。行锁总是锁住索引记录,即使表没有定义索引。对于这种情况,InnoDB 会创建一个隐藏的聚集索引,行锁在该索引上加锁。看 Section 14.6.2.1, “Clustered and Secondary Indexes”

事务数据格式类似于 SHOW ENGINE INNODB STATUSInnoDB monitor 的输出

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 or enable the innodb_locks_unsafe_for_binlog system variable (which is now deprecated). Under these circumstances, 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 or enabling innodb_locks_unsafe_for_binlog. 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 可以持有一个共享间隙锁(gap S-lock),事务 B 可以在同一个间隙上持有排他间隙锁(gap X-lock)。原因是,如果从索引中清除一条记录,则必须合并由不同事务保留在记录上的间隙锁。

在 InnoDB 中间隙锁是“纯粹的抑制性锁”,这就意味着它的目的仅仅是为了防止其他事务在间隙中插入值。间隙锁是可以共存的,多个事务可以在同一个间隙持有间隙锁。共享间隙锁和排他间隙锁之间不冲突,它们都执行相同的功能。

间隙锁可以被禁用。如果你改变了事务等级为读提交 或者开启innodb locks unsafe for binlog 系统变量(现在已经弃用)。在这些情况下,对于搜索和索引扫描间隙锁将被禁用,仅仅用于外键约束检查和重复键检查。

使用读提交 隔离级别或者开启innodb locks unsafe for binlog 系统变量还会有其他影响。 MySQL评估WHERE条件后,将释放不匹配行的记录锁。 对于UPDATE语句,InnoDB进行“semi-consistent”读取,以便将最新的提交版本返回给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 14.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;;

Next-Key Locks

next-key lock 是索引的行锁和在索引记录前间隙的间隙锁组合成的。

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

假设一个索引包含值 10,11,13 和 20。那么可能的 next-key locks 有以下间隔,是一个前开后闭区间:

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

对于上面的最后一个间隔,next-key lock 锁住了索引最大值的上限,这个最大值高于索引记录中的任何值。但是这个值不是一个真正的索引记录,因此,这个next-key lock 锁的是最大索引记录后的间隙。

默认情况下,InnoDB 引擎是可重复读 的事务级别。在这种情况下,InnoDB 使用 next-key locks 来查询和索引扫描。而且还能防止幻读(see Section 14.7.4, “Phantom Rows”)。

事务数据格式类似于 SHOW ENGINE INNODB STATUSInnoDB monitor 的输出

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.

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; ascf;;
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 STATUSInnoDB monitor 的输出

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; ascf;;
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 configuration option 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 14.6.1.6, “AUTO_INCREMENT Handling in InnoDB”.

自增锁

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

innodb_autoinc_lock_mode 参数配置控制自动增量锁定算法。它允许你在可预测的自动递增值序列和插入操作的最大并发性之间进行权衡。

更多的信息,看 Section 14.6.1.6, “AUTO_INCREMENT Handling in InnoDB”.

Predicate Locks for Spatial Indexes

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

Predicate Locks for Spatial Indexes

InnoDB 支持对包含空间列的列进行 SPATIAL 索引(see Section 11.4.8, “Optimizing Spatial Analysis”).

为了处理涉及 SPATIAL 索引的操作的锁定,next-key locking 不能很好地支持可重复读串行化 事务隔离级别。 多维数据中没有绝对排序概念,因此尚不清楚哪个是“next”键。

为了支持具有SPATIAL索引的表的隔离级别,InnoDB使用谓词锁。 SPATIAL索引包含最小边界矩形(MBR)值,因此InnoDB通过在用于查询的MBR值上设置谓词锁定来强制对索引进行一致的读取。 其他事务不能插入或修改将匹配查询条件的行。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值