论 MySql InnoDB 如何通过插入意向锁控制并发插入

10,140 阅读4分钟

前言

在讲解之前,先来思考一个问题——假设有用户表结构如下:

MySqlInnoDBRepeatable-Read:users(id PK, name, age KEY)

id name age
1 Mike 10
2 Jone 20
3 Tony 30

首先事务 A 插入了一行数据,并且没有 commit

INSERT INTO users SELECT 4, 'Bill', 15;

随后事务 B 试图插入一行数据:

INSERT INTO users SELECT 5, 'Louis', 16;

请问:

  1. 使用了什么锁?
  2. 事务 B 是否会被事务 A 阻塞?

预备知识

在了解插入意向锁之前,强烈建议先了解一下意向锁间隙锁

  1. 详解 MySql InnoDB 中的三种行锁
  2. 详解 MySql InnoDB 中意向锁的作用

插入意向锁(Insert Intention Locks)

首先让我们来看一下 MySql 手册 是如何解释 InnoDB 中的插入意向锁的:

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 操作产生的一种间隙锁。该锁用以表示插入意向,当多个事务在同一区间(gap)插入位置不同的多条数据时,事务之间不需要互相等待。假设存在两条值分别为 4 和 7 的记录,两个不同的事务分别试图插入值为 5 和 6 的两条记录,每个事务在获取插入行上独占的(排他)锁前,都会获取(4,7)之间的间隙锁,但是因为数据行之间并不冲突,所以两个事务之间并不会产生冲突(阻塞等待)。

总结来说,插入意向锁的特性可以分成两部分:

  1. 插入意向锁是一种特殊的间隙锁 —— 间隙锁可以锁定开区间内的部分记录。
  2. 插入意向锁之间互不排斥,所以即使多个事务在同一区间插入多条记录,只要记录本身(主键唯一索引)不冲突,那么事务之间就不会出现冲突等待

需要强调的是,虽然插入意向锁中含有意向锁三个字,但是它并不属于意向锁而属于间隙锁,因为意向锁表锁插入意向锁行锁

现在我们可以回答开头的问题了:

  1. 使用插入意向锁记录锁
  2. 事务 A 不会阻塞事务 B

为什么不用间隙锁

如果只是使用普通的间隙锁会怎么样呢?还是使用我们文章开头的数据表为例:

MySql,InnoDB,Repeatable-Read:users(id PK, name, age KEY)

id name age
1 Mike 10
2 Jone 20
3 Tony 30

首先事务 A 插入了一行数据,并且没有 commit

INSERT INTO users SELECT 4, 'Bill', 15;

此时 users 表中存在三把锁

  1. id 为 4 的记录行的记录锁
  2. age 区间在(10,15)的间隙锁
  3. age 区间在(15,20)的间隙锁

最终,事务 A 插入了该行数据,并锁住了(10,20)这个区间。

随后事务 B 试图插入一行数据:

INSERT INTO users SELECT 5, 'Louis', 16;

因为 16 位于(15,20)区间内,而该区间内又存在一把间隙锁,所以事务 B 别说想申请自己的间隙锁了,它甚至不能获取该行的记录锁,自然只能乖乖的等待 事务 A 结束,才能执行插入操作。

很明显,这样做事务之间将会频发陷入阻塞等待插入的并发性非常之差。这时如果我们再去回想我们刚刚讲过的插入意向锁,就不难发现它是如何优雅的解决了并发插入的问题。

总结

  1. MySql InnoDBRepeatable-Read 的事务隔离级别下,使用插入意向锁来控制和解决并发插入。
  2. 插入意向锁是一种特殊的间隙锁
  3. 插入意向锁锁定区间相同记录行本身不冲突的情况下互不排斥