MySQL InnoDB下一键锁定中唯一索引和非唯一索引之间差异的基本原理

时间:2014-10-06 11:15:22

标签: mysql locking innodb

MySQL InnoDB在事务中的非唯一索引上使用下一键锁定,从而锁定扫描索引之前和之后的间隙(由于MySQL手册无法以清晰的方式传达,手册页上的next-key locks表示只有被扫描索引之前的间隙被锁定:http://dev.mysql.com/doc/refman/5.7/en/innodb-record-level-locks.html)。

但是,我无法理解这背后的整个理由......

二手设置:

CREATE TABLE test (a int, b int, index (a));
INSERT INTO test VALUES (5,5), (10,10), (15,15);

首先连接的客户端启动事务A并发出以下UPDATE查询:

UPDATE test set b = 10 where a = 10;

从下一个传入连接启动事务B运行以下查询会得到以下结果:

INSERT INTO test VALUES(5,5); //On hold
INSERT INTO test VALUES(9,9); //On hold
INSERT INTO test VALUES(14,14); //On hold
INSERT INTO test VALUES(4,4); //Works
INSERT INTO test VALUES 15,15); //Works
UPDATE test SET a = 1 WHERE a = 5; //Works
UPDATE test SET a = 8 WHERE a = 5; //On hold
UPDATE test SET a = 7 WHERE a = 15; //On hold
UPDATE test SET a = 100 WHERE a = 15; //Works

似乎事务B不能插入行,其中a是[5,15)(5包含 - 15除外),也不能修改现有行并将a设置为(5,15)(5除外 - 15除外)

现在,将列a更改为PRIMARY KEY代替:

ALTER TABLE test DROP INDEX a;
ALTER TABLE test ADD PRIMARY KEY (a);

重做以上在事务B中运行的结果现在给出了以下结果(第5行和第15行的插入给出了关于重复键的错误,这就是为什么它们不包括在内):

INSERT INTO test VALUES(9,9); //Works
INSERT INTO test VALUES(14,14); //Works
INSERT INTO test VALUES(4,4); //Works
INSERT INTO test VALUES(10,10); //On hold
UPDATE test SET a = 1 WHERE a = 5; //Works
UPDATE test SET a = 8 WHERE a = 5; //Works
UPDATE test SET a = 7 WHERE a = 15; //Works
UPDATE test SET a = 100 WHERE a = 15; //Works
UPDATE test SET a = 10 WHERE a = 15; //On hold
UPDATE test SET a = 100 WHERE a = 10; //On hold

使用主键的行为似乎完全可以理解,我不会质疑它(即使缺少间隙锁,使用间隙锁来防止幻像读取的原理,也不会阻止幻像读取)。我根本不会质疑这种行为,我只是很难理解常规索引的处理方式以及为什么以不同的方式处理它们。)

问题:

  1. 使用我们想要阻止幻像读取的下一键锁定的原因(这似乎意味着遵守SELECT查询不应在整个过程中返回不同结果的规则隔离级别REPEATABLE READ)中的事务,还是因为InnoDB推断用户可能希望插入接近查询结果(这将是对该查询结果的启发式服务)用户)?第三个原因可能是整个系统原理似乎是锁定查询导致的任何行,并且InnoDB不加考虑地执行此操作(这将遵循关于并发规则的一些总体原则) 。从http://dev.mysql.com/doc/refman/5.7/en/innodb-next-key-locking.html开始,似乎只有在WHERE子句具有a > 10条件时,才会使用下一键锁定来防止幻像读取,这样才有意义,但如果是这样,为什么接下来呢?当WHERE子句特定地解决某些行时,是否也应用了-key锁定?也许有几个分离的原因?
  2. 鉴于下一键锁定的原因,当列具有唯一索引与非唯一索引时,为什么必须具有不同的行为?至少上面提出的前两个原因似乎没有要求这个,即使第三个可能是InnoDB在列具有非唯一索引时必须搜索更多行。否则,对我来说,无论列是否具有非唯一索引或唯一索引,似乎都可能想要插入一个近似行...另一方面,当更新行时,有没有理由相信用户会想要在附近插入一行,为什么不锁定整个表为什么你在...?
  3. 为什么a = 5INSERT被锁定,UPDATE没有?好像有两个锁定原则同时发挥作用,一个锁定现有行的修改,另一个锁定插入,现有行a = 5未锁定但插入行a = 5锁住了。这是正确的吗?如果是这样,为什么索引5包含在插入的间隙锁中?
  4. 我的MySQL版本为5.5.24,我使用默认隔离级别REPEATABLE READ

1 个答案:

答案 0 :(得分:1)

你的问题太多了。 :)

我不是数据库专家,我只是给你一些提示。

A)。索引约束不一定是唯一性。当条件列没有索引或没有唯一性时,MySQL使用间隙锁。因为主键是唯一性索引,所以它只锁定选定的记录。

B)。当您更新索引列时,实际上记录需要重新索引。因为innodb使用聚簇索引,这意味着记录位于主索引B +树的叶子上。因此,当需要找到放置更新的索引节点的位置时,数据库需要授予锁定请求。

UPDATE test SET a = 10 WHERE a = 15; //On hold

a = 15时没有锁定,但是当你想将索引置于a = 10时,那里存在锁定。所以它持有。