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
使用主键的行为似乎完全可以理解,我不会质疑它(即使缺少间隙锁,使用间隙锁来防止幻像读取的原理,也不会阻止幻像读取)。我根本不会质疑这种行为,我只是很难理解常规索引的处理方式以及为什么以不同的方式处理它们。)
问题:
SELECT
查询不应在整个过程中返回不同结果的规则隔离级别REPEATABLE READ
)中的事务,还是因为InnoDB推断用户可能希望插入接近查询结果(这将是对该查询结果的启发式服务)用户)?第三个原因可能是整个系统原理似乎是锁定查询导致的任何行,并且InnoDB不加考虑地执行此操作(这将遵循关于并发规则的一些总体原则) 。从http://dev.mysql.com/doc/refman/5.7/en/innodb-next-key-locking.html开始,似乎只有在WHERE
子句具有a > 10
条件时,才会使用下一键锁定来防止幻像读取,这样才有意义,但如果是这样,为什么接下来呢?当WHERE
子句特定地解决某些行时,是否也应用了-key锁定?也许有几个分离的原因?a = 5
行INSERT
被锁定,UPDATE
没有?好像有两个锁定原则同时发挥作用,一个锁定现有行的修改,另一个锁定插入,现有行a = 5
未锁定但插入行a = 5
锁住了。这是正确的吗?如果是这样,为什么索引5包含在插入的间隙锁中?我的MySQL版本为5.5.24
,我使用默认隔离级别REPEATABLE READ
。
答案 0 :(得分:1)
你的问题太多了。 :)
我不是数据库专家,我只是给你一些提示。
A)。索引约束不一定是唯一性。当条件列没有索引或没有唯一性时,MySQL使用间隙锁。因为主键是唯一性索引,所以它只锁定选定的记录。
B)。当您更新索引列时,实际上记录需要重新索引。因为innodb使用聚簇索引,这意味着记录位于主索引B +树的叶子上。因此,当需要找到放置更新的索引节点的位置时,数据库需要授予锁定请求。
UPDATE test SET a = 10 WHERE a = 15; //On hold
a = 15时没有锁定,但是当你想将索引置于a = 10时,那里存在锁定。所以它持有。