InnoDB锁和事务模型之锁

1 问题背景

前面基本从整体上了解了InnoDB的架构以及若干个常听到的概念,比如undo log、redo log。今天研究常讲到的锁,比如共享锁、排他锁、意向锁、间隙锁等等。

2 前言

本片博客仅从概念级别切入,后续再深入研究

InnoDB锁和事务模型将从以下几个方面学习:

  • InnoDB锁
  • InnoDB事务模型
  • InnoDB中不同的SQL语句设置的锁
  • 虚拟行
  • InnoDB中的死锁
  • 事务调度

3 InnoDB锁有哪些

  • 共享锁(Shared Locks)、排他锁(Exclusive Locks)
  • 意向锁(Intention Locks)
  • 记录锁(Record Locks)
  • 间隙锁(Gap Locks)
  • Next-Key锁(Next-Key Locks)
  • 插入意向锁(Insert Intention Locks)
  • 自增锁(AUTO-INC Locks)
  • 用于空间索引的谓词锁(Predicate Locks for Spatial Indexes)

4 共享锁、排他锁

原文
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.

InnoDB实现标准的行级锁有2种,分别是共享锁(简称S锁)排他锁(X锁)

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

原文
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.

如果事务T1持有一个行r的共享锁,那么来自另外的事务T2请求持有行r的锁将会以如下处理:

  • 事务T2请求S锁将会被立即授予。因此,事务T1和事务T2都会持有行r的S锁
  • 事务T2请求X锁将会被立即授予。

原文
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.

如果事务T1持有行r的排他锁,另外的事务T2 不管请求 什么类型的锁都不会被授予。事务T2不得不等待事务T1释放行r的排他锁。

5 意向锁

在介绍意向锁之前,先给出官方的意向锁概念:

原文
intention lock
A kind of lock that applies to the table, used to indicate the kind of lock the transaction intends to acquire on rows in the table. Different transactions can acquire different kinds of intention locks on the same table, but the first transaction to acquire an intention exclusive (IX) lock on a table prevents other transactions from acquiring any S or X locks on the table. Conversely, the first transaction to acquire an intention shared (IS) lock on a table prevents other transactions from acquiring any X locks on the table. The two-phase process allows the lock requests to be resolved in order, without blocking locks and corresponding operations that are compatible. For more information about this locking mechanism, see Section 15.7.1, “InnoDB Locking”.

意向锁,是一种应用于表的锁,也就是 表级锁,用于表明事务将要请求表中某些行的锁类型。不同事务可以在同一张表上获取不同类型的意向锁,但是第一个在表上获取到意向排他锁(IX锁)的事务会阻止其他事务请求该表的任何S锁以及X锁。相反地,第一个获取到该表的意向共享锁(IS锁)会阻止其他事务获取该表的任何X锁。两阶段过程允许按序解决锁请求,不会阻塞兼容的锁以及相应的操作。

原文
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.

InnoDB允许多种粒度的锁,这些锁允许行锁和表锁共存。比如,一个语句LOCK TABLES ... WRITE会给指定的表加排他锁。为了能有多粒度的锁,InnoDB使用意向锁。意向锁是表级锁,它指明稍后的事务对表中的某行请求什么类型的锁(S锁或者X锁)。有以下两种类型的意向锁:

  • 一个 共享意向锁(简称IS 意味着一个事务将会对表中的指定行设置一个共享锁
  • 一个 共享排他锁(简称IX 意味着一个事务将会对表中的指定行设置一个排他锁

原文
For example, SELECT … FOR SHARE sets an IS lock, and SELECT … FOR UPDATE sets an IX lock.

比如SELECT ... FOR SHARE设置一个共享意向锁(IS锁)SELECT ... FOR UPDATE设置一个共享排他锁(IX锁)

原文
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.

意向锁的协议如下:

  • 在一个事务获取到表中某行的共享锁之前,它必须排在第一获取到表中的共享意向锁或更强的锁
  • 在一个事务获取到表中某行的排他锁前,它必须第一获取到表中的共享排他锁

表级的锁类型兼容性如下矩阵所示:

XIXSIS
XConflictConflictConflict
IXConflictCompatibleConflict
SConflictConflictCompatible
ISConflictCompatibleCompatible

注:Conflict-冲突;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.

意向锁不会阻塞任何事物,除了全表请求以外(比如LOCK TABLES ... WRITE)。意向锁的主要目的是展示某人正在锁住一行,或者将要锁住表中的某行。

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

对于意图锁定事务数据出现类似于在下面SHOW ENGINE INNODB STATUS和 InnoDB的监视器 输出:

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

官方文档介绍了很多,然而笔者还是不懂,因此搜索知乎找到似乎比较正确的理解,如下:详情见关于知乎回答的意向锁

我最近也在看这个,我说一下我的理解

①在mysql中有表锁,

LOCK TABLE my_tabl_name READ; 用读锁锁表,会阻塞其他事务修改表数据。

LOCK TABLE my_table_name WRITe; 用写锁锁表,会阻塞其他事务读和写。

②Innodb引擎又支持行锁,行锁分为

共享锁,一个事务对一行的共享只读锁。

排它锁,一个事务对一行的排他读写锁。


③这两中类型的锁共存的问题

考虑这个例子:

事务A锁住了表中的一行,让这一行只能读,不能写。

之后,事务B申请整个表的写锁。

如果事务B申请成功,那么理论上它就能修改表中的任意一行,这与A持有的行锁是冲突的。

数据库需要避免这种冲突,就是说要让B的申请被阻塞,直到A释放了行锁。

数据库要怎么判断这个冲突呢?

step1:判断表是否已被其他事务用表锁锁表

step2:判断表中的每一行是否已被行锁锁住。

注意step2,这样的判断方法效率实在不高,因为需要遍历整个表。

于是就有了意向锁。

在意向锁存在的情况下,事务A必须先申请表的意向共享锁,成功后再申请一行的行锁。

在意向锁存在的情况下,上面的判断可以改成

step1:不变

step2:发现表上有意向共享锁,说明表中有些行被共享行锁锁住了,因此,事务B申请表的写锁会被阻塞。


注意:申请意向锁的动作是数据库完成的,就是说,事务A申请一行的行锁的时候,数据库会自动先开始申请表的意向锁,不需要我们程序员使用代码来申请。

6 记录锁

原文
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.

记录锁是一条索引记录上的锁。比如,SELECT c1 FROM t WHERE c1 = 10 FOR UPDATE;阻止任何事务插入、更新或者删除t.c1 = 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”.

即使一个表没有定义索引,记录锁总是锁住索引记录。比如,InnoDB创建一个隐藏的聚集索引并使用这个索引作为记录锁。详情见“Clustered and Secondary Indexes”

7 间隙锁

原文
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列,不管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.

如果id列没有被索引或者具有非唯一索引,那么该语句会锁定前面的间隙

原文
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.

这里值得注意的是不同的事务可以在间隙上持有冲突的锁。比如,事务A持有一个共享间隙锁(间隙S锁)在一个间隙上,事务B持有一个排他间隙锁(间隙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是纯粹禁止间隙锁的,这意味着他们唯一的目的是阻止其他事务插入到间隙中。间隙锁可以共存。一个事务采用的间隙锁不会阻止另一个事务在同一个间隙上采用间隙锁。共享和排他间隙锁之间没有区别。他们彼此不冲突,并且执行相同的功能。

原文
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做一个“半一致性”读,这样它返回最新提交的MySQL版本以至于可以决定UPDATE语句的WHERE条件是否匹配该行。

8 Next-Key锁

原文
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.

next-key锁是索引记录上的记录锁和索引记录之前的间隙的间隙锁的组合。

原文
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.

InnoDB执行行级锁的方式是,当他搜索或者扫描表索引时,它会在遇到的索引记录上设置共享锁或排他锁。因此,行级锁实际上是索引记录锁。一个next-key锁在索引记录上也影响索引之前的“间隙”。也就是说,一个next-key锁是一个索引记录锁加上一个在索引记录之前的间隙上的间隙锁。如果一个会话有共享锁或者排他锁在R记录的索引上,另外的会话不可以立刻在R记录的索引顺序中紧接在前的间隙中插入新的索引记录。

原文
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:

假设一个索引包含10、11、13和20值。该索引的next-key锁覆盖以下区间,其中圆括号排除区间端点,方括号包含端点:

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

最后的区间中,next-key锁锁定索引中的最大值以上的间隙,并且“supremum"伪记录的值高于索引中任何实际值。supremum不是真正的索引记录,因此,实际上,next-key锁只锁定最大索引值之后的间隙。

原文
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”).

默认情况下,InnoDB在可重复读的隔离级别运行。在这种情况下,InnoDB使用next-key锁进行搜索和索引扫描,以防止幻行。

9 插入意向锁

原文
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.

插入意向锁是行插入之前的INSERT操作设置的间隙锁。此锁意味着插入意向锁,如果多个事务插入到同一个索引间隙的不同位置,则他们无需互相等待。假设存在值为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.

下面的例子阐述了在获取到插入行的排他锁之前采用插入意向锁的事务。该例子涉及到两个客户端A和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创建一个包含两个索引记录(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开启一个事务来插入记录到间隙中。在等待获取到排他锁之前,事务采用插入意向锁。

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

用于插入意图锁定事务数据出现类似于在下面 SHOW ENGINE INNODB STATUS和 InnoDB的监视器 输出:

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

10 AUTO-INC锁

原文
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.

AUTO-INC是一种特殊的表级锁,表示事务插入到表中的AUTO_INCREMENT列的锁。在最简单的情况下,如果一个事务插入值到表中,任何其他事务必须等待他们自己插入到表中,以便第一个事务插入的行接收连续的主键值。

原文
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.

innodb_autoinc_lock_mode变量用于控制自增锁的算法。它允许你选择如何在可预测的自增序列值和插入操作的最大并发之间进行权衡。

11 空间索引谓词锁

原文
InnoDB supports SPATIAL indexing of columns containing spatial data (see Section 11.4.9, “Optimizing Spatial Analysis”).

InnoDB支持SPATIAL对包含空间数据的列进行索引。

原文
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.

为了处理涉及空间索引的锁操作,next-key锁不能很好地支持可重复读或者序列化的事务隔离级别。多维数据中没有绝对的排序的概念,所以不清楚哪个是“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使用为此锁。一个包含最小外接矩阵(MBR)值的空间索引,InnoDB通过设置一个用于查询的MBR值的谓词锁进行一致性读取。其他事务不可以插入、修改匹配到的查询条件。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值