我想在非主键字段中插入具有唯一值的行。我不能使用唯一索引(因为多个字段索引中的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,我不想更改数据库的默认设置,因为其他应用程序使用它。
答案 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(“交错”锁定模式)