使用主键和唯一键对表进行意外锁定

Jak*_*raw 9 mysql database performance innodb locking

我遇到了一个innodb锁定问题,用于表上具有主键和单独唯一索引的事务.似乎TX使用唯一键删除记录,然后重新插入相同的记录,这将导致下一键锁定而不是预期的记录锁定(因为该键是唯一的).请参阅下面的测试用例以及我希望锁定哪些记录的细分:

DROP TABLE IF EXISTS foo; 
CREATE TABLE `foo` ( 
  `i` INT(11) NOT NULL, 
  `j` INT(11) DEFAULT NULL, 
  PRIMARY KEY (`i`), 
  UNIQUE KEY `jk` (`j`) 
) ENGINE=InnoDB DEFAULT CHARSET=latin1 ; 
INSERT INTO foo VALUES (5,5), (8,8), (11,11); 
Run Code Online (Sandbox Code Playgroud)

(注意:只需在TX1 sql之后运行TX2 sql,在单独的连接中)

TX1

START TRANSACTION; 
DELETE FROM foo WHERE i=8; 
Run Code Online (Sandbox Code Playgroud)

导致i = 8的独占锁定(没有间隙锁定,因为我是主键并且是唯一的)

INSERT INTO foo VALUES(8,8); 
Run Code Online (Sandbox Code Playgroud)

导致i = 8&j = 8的独占锁定,i = 6&i = 7的共享意图锁定,以及j = 6&j = 7

TX2

START TRANSACTION; 
INSERT INTO foo VALUES(7,7); 
Run Code Online (Sandbox Code Playgroud)

导致i = 7&j = 7的排他锁定,以及i = 6&j = 6的共享意图锁定

我希望TX2不被TX1阻止,但它确实如此.奇怪的是,阻塞似乎与TX1的插入有关.我这样说是因为如果在删除后没有运行TX1的insert语句,TX2的插入不会被阻止.这几乎就像TX1重新插入(8,8)导致索引j上的下一键锁定(6,8).

任何见解都会非常感激.

elG*_*LLa 6

您遇到的问题是因为MySQL不仅仅锁定要插入的值的表行,它会id按顺序锁定上一个和下一个id 之间的所有可能值,因此,重复使用下面的示例:

DROP TABLE IF EXISTS foo;
CREATE TABLE `foo` (
  `i` INT(11) NOT NULL,
  `j` INT(11) DEFAULT NULL,
  PRIMARY KEY (`i`),
  UNIQUE KEY `jk` (`j`) 
) ENGINE=InnoDB DEFAULT CHARSET=latin1 ;
INSERT INTO foo VALUES (5,5), (8,8), (11,11);
Run Code Online (Sandbox Code Playgroud)

假设您从事务TX1开始:

START TRANSACTION;
REPLACE INTO foo VALUES(8,8);
Run Code Online (Sandbox Code Playgroud)

然后,如果您开始交易TX2,将锁定5或11之间的任何INSERTREPLACE使用id:

START TRANSACTION;
REPLACE INTO foo VALUES(11,11);
Run Code Online (Sandbox Code Playgroud)

看起来MySQL使用这种锁定来避免这里描述的"幻象问题":http://dev.mysql.com/doc/refman/5.0/en/innodb-next-key-locking.html,MySQL使用" next-key locking",它将索引行锁定与间隙锁定结合起来,这对我们来说意味着它将锁定前一个和下一个id之间的许多可能的id,并且还会锁定prev和next id.

为了避免这种情况,请尝试创建一个插入记录的服务器算法,以便插入不同事务的记录不会重叠,或者至少不会同时执行所有事务,因此TX不必彼此等待.

  • 不,mySQL不会为聚簇索引插入发出间隙锁(它会发出一个插入意图间隙锁,它不会阻止间隙中的并发插入).这似乎与删除和随后插入到聚簇和非聚集索引(删除标记以及后续删除 - 未标记)有关,这导致双间隙锁定(这是您注意到的)但似乎没有必要对我来说. (2认同)