带主键的表意外锁定&独特的关键

时间:2011-01-31 15:50:50

标签: mysql database performance locking innodb

对于包含主键和单独唯一索引的表上的事务,我遇到了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); 

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

TX1

START TRANSACTION; 
DELETE FROM foo WHERE i=8; 

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

INSERT INTO foo VALUES(8,8); 

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

TX2

START TRANSACTION; 
INSERT INTO foo VALUES(7,7); 

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

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

非常感谢任何见解。

3 个答案:

答案 0 :(得分: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);

假设您从事务TX1开始:

START TRANSACTION;
REPLACE INTO foo VALUES(8,8);

然后,如果您开始投放TX2,则使用{1}}或INSERT在5到11之间的任何内容都将被锁定:

REPLACE

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

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

答案 1 :(得分:4)

似乎问题可能在于InnoDB索引很奇怪。

主键(群集)为i,并且会有rowid与之关联。

j上的唯一键(非聚集)的rowid ij的值DELETE相关联。

INSERT的相同键值执行i后跟rowid,应该会为主键(群集)生成即将到来的rowid,同样,即将到来的不同jtx_isolation(非聚集)的值相关联。

这需要在MVCC机制中进行一些奇怪的内部锁定。

您可能需要将事务隔离级别更改为允许脏读(即,没有可重复读取)

在会话中使用READ_COMMITTED变量玩一些游戏 试试READ_UNCOMMITTEDtransaction_isolation=read-committed

Click here to see syntax for setting Isolation Level in a Session
Click here to see how there was once a bug concerning this within a Session and the warning on how to use it carefully

否则,只需在/etc/my.cnf(示例)

中永久设置以下内容

的[mysqld]
{{1}}

试一试!!!

答案 2 :(得分:0)

https://bugs.mysql.com/bug.php?id=68021

此错误问题回答了您的问题。

这是InnoDB的设计缺陷,上游用于解决此问题,以避免在读提交隔离状态下row_ins_scan_sec_index_for_duplicate中的间隙锁定。但是,它带来了另一个问题,该修复程序导致二级索引唯一键冲突默默地发生,因此上游恢复了此修复程序。