【问题标题】:MySQL - why one insert locks second insert?MySQL - 为什么一个插入锁定第二个插入?
【发布时间】:2014-05-15 11:21:51
【问题描述】:

我想在非主键字段中插入具有唯一值的行。我不能使用唯一索引(因为多字段索引中的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);

如您所见,我正在插入独立值。但是第二个会话被第一个会话锁定。为什么?可能是因为gap/next-key lock。

我怎样才能插入“手动”避免重复键且没有相互锁定的记录?

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

【问题讨论】:

  • 这似乎是一种相当复杂的 INSERT IGNORE 方法。您采用这种方法的原因是什么?
  • 因为我可以在某些唯一索引列中有 NULL。你知道我还能如何插入唯一值吗?

标签: mysql locking deadlock


【解决方案1】:

关于SELECT FOR UDPATE的间隙锁,有唯一索引的锁、非唯一索引的间隙锁和没有索引的表锁:

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

此外,InnoDB 在使用 auto_increment 时会放置锁以保留键序列,这在复制期间很重要。

这是来自documentation(强调我的):

当访问自增计数器时,InnoDB 使用一个特殊的 保持到当前 SQL 结尾的表级 AUTO-INC 锁 声明,不至于交易结束。特殊锁释放 引入策略以提高插入到 包含 AUTO_INCREMENT 列的表。然而,两个 事务不能在同一个表上拥有 AUTO-INC 锁 同时,如果 AUTO-INC 可能会对性能产生影响 锁被持有很长时间。声明可能就是这种情况 例如插入所有行的 INSERT INTO t1 ... SELECT ... FROM t2 从一张桌子到另一张桌子。

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

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

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

【讨论】:

    猜你喜欢
    • 2018-11-02
    • 1970-01-01
    • 1970-01-01
    • 2018-04-24
    • 2022-01-07
    • 2014-11-26
    • 2017-02-27
    • 2015-01-15
    相关资源
    最近更新 更多