MySQL - 为什么一个插入锁定第二个插入?

时间:2014-04-03 14:33:39

标签: mysql locking deadlock

我想在非主键字段中插入具有唯一值的行。我不能使用唯一索引(因为多个字段索引中的NULL值,但这里不重要)。

我使用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,我不想更改数据库的默认设置,因为其他应用程序使用它。

1 个答案:

答案 0 :(得分: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(“交错”锁定模式)