2014-04-03 58 views
0

我想在非主键字段中插入具有唯一值的行。我无法使用唯一索引(因为多个字段索引中的NULL值,但这里并不重要)。MySQL - 为什么一个插入锁定第二个插入?

我使用INSERT/SELECT WHERE NOT EXISTS作为原子(我希望)操作 - 请参见下文。

我将描述问题上简化的例子:

我有一个空表:

CREATE TABLE Test ( Id int(11) NOT NULL AUTO_INCREMENT, A int(11) DEFAULT NULL, PRIMARY KEY (Id) ) ENGINE=InnoDB;

...和2个会话与自动提交模式关闭:

在第一会话我执行:

INSERT INTO Test (A) SELECT A FROM (SELECT 1 AS A) Inn WHERE NOT EXISTS (SELECT A FROM Test WHERE A = 1 FOR UPDATE);

在第二届会议上我执行:

INSERT INTO Test (A) SELECT A FROM (SELECT 2 AS A) Inn WHERE NOT EXISTS (SELECT A FROM Test WHERE A = 2 FOR UPDATE);

正如你所看到的,我将独立的值。但是第二次会议被第一次会议锁定。为什么?也许这是因为间隙/下一个键锁定。

我怎么能插入记录与“手动”避免重复键和没有相互锁定?

默认的事务隔离级别是REPEATABLE READ,我不想更改数据库的默认设置,因为其他应用程序使用它。

+0

这似乎是一个非常复杂的方法来执行INSERT IGNORE。这种方法的理由是什么? – Arnout

+0

因为我可以在一些唯一索引列中有NULL。你有什么想法我可以插入唯一值吗? – Marek

回答

1

关于差距锁定为SELECT FOR UDPATE,有锁具有唯一索引,差距锁与非唯一索引,表锁没有索引:

How do I lock on an InnoDB row that doesn't exist yet?

另外,InnoDB的地方使用auto_increment保留键序列时会锁定,这在复制期间很重要。

这是从documentation(重点煤矿):

当访问自动增长计数器,InnoDB使用一种特殊的 表级的AUTO-INC锁,它保持当前的SQL的结束 声明,而不是交易结束。引入了特殊锁定版本 以提高并发性,将并发插入包含AUTO_INCREMENT列的 表中。尽管如此,两个 交易不能同时在同一个表 上具有AUTO-INC锁定,如果长时间保持AUTO-INC 锁,这会对性能产生影响。对于诸如INSERT INTO t1 ... SELECT ... FROM t2的陈述 可能是这种情况,其将 的所有行从一个表插入到另一个表中。

在MySQL v5.1.22中有一些性能改进,它可能使用更快的方法,但在语句之间仍有一些等待。

但是,如果不使用复制,则可以通过使交错的auto_increment值提高性能从而提高并发性能:

innodb_autoinc_lock_mode = 2(“交错”锁定模式)