MySQL核心原理与性能优化-InnoDB锁定和事务模型

说明:【引用块】内是官方文档,对应下面的【非引用块】部分为翻译。

14.7 InnoDB Locking and Transaction Model

        InnoDB锁定和事务模型

14.7.1 InnoDB Locking

14.7.2 InnoDB Transaction Model

14.7.3 Locks Set by Different SQL Statements in InnoDB

14.7.4 Phantom Rows

14.7.5 Deadlocks in InnoDB

14.7.1 InnoDB锁定

14.7.2 InnoDB事务模型

14.7.3由InnoDB中的不同SQL语句设置的锁

14.7.4幻影行

14.7.5 InnoDB中的死锁

To implement a large-scale, busy, or highly reliable database application, to port substantial code from a different database system, or to tune MySQL performance, it is important to understand InnoDB locking and the InnoDB transaction model.

为了实现大规模,繁忙或高度可靠的数据库应用程序,从不同的数据库系统移植大量代码,或调整MySQL性能,了解InnoDB锁定和InnoDB 事务模型非常重要 。

This section discusses several topics related to InnoDB locking and the InnoDB transaction model with which you should be familiar.

本节讨论的是InnoDB锁定和InnoDB 事务模型相关的几个主题 ,您应该熟悉这些主题。

【下面详细说明】


14.7.1 InnoDB Locking(InnoDB锁)

This section describes lock types used by InnoDB.

本节介绍所InnoDB使用的锁定类型 。

Shared and Exclusive Locks

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

  • 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持有对row r的共享(S)锁,则来自某些不同事务T2 对行r锁的请求将按以下方式处理:

  • T2对 S锁的请求可以立即被授予。其结果是,T1T2 都持有r得S锁。

  • T2对 X锁的请求不能立即授予。

如果某个事务T1在行r上具有排他(X)锁,则不能立即批准某个不同事务T2对行r上任一类型的锁的请求。相反,事务T2必须等待到事务T1释放对row 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 ... 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.

意向锁

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

  • 意向共享锁IS)指示一个事务打算在表中设置各行上一个共享锁。

  • 意向独占锁IX)指示一个事务打算在表中设定各行上一个排他锁。

例如,SELECT ... LOCK IN SHARE MODE设置一个IS锁,然后SELECT ... FOR UPDATE设置一个IX锁。

意向锁定协议如下:

  • 在事务可以获取表中某行的共享锁之前,它必须首先获取表中的IS锁或更高级别的锁。

  • 在事务可以获取表中某行的排它锁之前,它必须首先获取该表中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

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:

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

意向锁不会阻止除全表请求(例如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 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:

记录锁

记录锁定是对索引记录的锁定。例如, SELECT c1 FROM t WHERE c1 = 10 FOR UPDATE; 可以防止任何其它事务插入,更新或删除值的t.c1是10的行。

记录锁始终锁定索引记录,即使没有定义索引的表也是如此。在这种情况下,InnoDB创建一个隐藏的聚集索引,并将该索引用于记录锁定。请参见 第14.6.2.1节“聚集索引和二级索引”

SHOW ENGINE INNODB STATUS和 InnoDB的监视器 中,记录锁的事务数据显输出示如下:

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 c1 FROM t WHERE c1 BETWEEN 10 and 20 FOR UPDATE;阻止其他事务将value 15插入column中t.c1,无论该列 中是否已有这样的值,因为该范围中所有现有值之间的间隙都是锁定的。

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

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

对于使用唯一索引锁定行以搜索唯一行的语句,不需要间隙锁定。(这不包括搜索条件只包含多列唯一索引的某些列的情况;在这种情况下,确实会发生间隙锁定。)例如,如果该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.

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.

如果id未建立索引或索引不唯一,则该语句会锁定前面的间隙。

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

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

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

使用READ COMMITTED隔离级别或启用 innodb_locks_unsafe_for_binlog 还具有其他效果。MySQL评估WHERE条件后,将释放不匹配行的记录锁。对于 UPDATE语句,InnoDB 执行“半一致”读取,以便将最新的提交版本返回给MySQL,以便MySQL可以确定行是否与UPDATEWHERE 条件匹配。

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:

Next-Key Locks 键锁是索引记录上的记录锁和索引记录之前的间隙上的间隙锁的组合。

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

假定索引包含值10、11、13和20。此索引的可能的Next-Key Locks涵盖以下间隔,其中,圆括号表示排除区间端点,方括号表示包括端点:

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

对于最后一个间隔,Next-Key Locks锁定索引中最大值上方的间隙,以及值高于索引中任何实际值的“上界”伪记录。上界值不是真正的索引记录,因此,实际上,Next-Key Locks只锁定最大索引值后面的间隙。

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

SHOW ENGINE INNODB STATUS和 InnoDB的监视器 中,Next-Key Locks的事务数据显示如下:

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:

插入意向锁

插入意向锁定是一种通过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 |
+-----+

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.

客户B开始交易将记录插入间隙。事务在等待获得排他锁的同时获取插入意向锁。

<span style="color:#555555"><span style="color:#000000">


</span></span>
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和 InnoDB的监视器中显示如下:

<span style="color:#555555"><span style="color:#000000">


</span></span>
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 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-INC锁是通过使用AUTO_INCREMENT插入表事务取得一个特殊的表级锁 。在最简单的情况下,如果一个事务正在向表中插入值,那么任何其他事务都必须等待该表中进行插入操作,以便第一个事务插入的行接收连续的主键值。

innodb_autoinc_lock_mode 用于配置自增加锁的算法选项控制。它允许您选择如何在可预测的自动递增值序列和插入操作的最大并发性之间进行权衡。

有关更多信息,请参见 第14.6.1.6节“ InnoDB中的AUTO_INCREMENT处理”

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.

空间索引的谓词锁

InnoDB支持SPATIAL 包含空间列的列的索引(请参见 第11.4.8节“优化空间分析”)。

要处理涉及SPATIAL索引的操作的锁定 ,下一键锁定不能很好地支持支持REPEATABLE READ或 SERIALIZABLE事务隔离级别。多维数据中没有绝对排序概念,因此尚不清楚哪个是 “下一个”键。

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

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

猜你喜欢

转载自blog.csdn.net/Coder_Boy_/article/details/110149124