对于包含主键和单独唯一索引的表上的事务,我遇到了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,在单独的连接中)
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
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)。
非常感谢任何见解。
答案 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
i
与j
的值DELETE
相关联。
对INSERT
的相同键值执行i
后跟rowid
,应该会为主键(群集)生成即将到来的rowid
,同样,即将到来的不同j
与tx_isolation
(非聚集)的值相关联。
这需要在MVCC机制中进行一些奇怪的内部锁定。
您可能需要将事务隔离级别更改为允许脏读(即,没有可重复读取)
在会话中使用READ_COMMITTED
变量玩一些游戏
试试READ_UNCOMMITTED
和transaction_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中的间隙锁定。但是,它带来了另一个问题,该修复程序导致二级索引唯一键冲突默默地发生,因此上游恢复了此修复程序。