INSERT ... SELECT,InnoDB和锁定

时间:2014-01-31 15:48:00

标签: mysql sql innodb

我在MySQL 5.5.34(在Ubuntu 12.04上)使用InnoDB引擎时遇到了以下行为。

执行INSERT ... SELECT语句时,一些意外的行似乎被锁定在正在读取的表中。

让我举个例子。假设两个表table_sourcetable_dest具有以下结构(特别注意索引):

CREATE TABLE table_source (
  id int(11) unsigned NOT NULL AUTO_INCREMENT,
  group_id int(11) NOT NULL,
  data text NOT NULL,
  created timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (id),
  KEY group_id_created (group_id,created)
) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8;

CREATE TABLE table_dest (
  id int(11) unsigned NOT NULL AUTO_INCREMENT,
  group_id int(11) NOT NULL,
  data text NOT NULL,
  created timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (id),
  KEY group_id_created (group_id,created)
) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8;

假设我现在执行以下事务:

BEGIN;
INSERT INTO table_dest 
    SELECT * FROM table_source WHERE group_id = 3 AND created < '2014-01-04';
....

然后使用INSERT 2:

似乎锁定了group_id的源表
INSERT INTO table_source (group_id, data, created) 
    VALUES (2, 'data', NOW()); --< This locks

以下是其他一些陈述,如果它们锁定与否:

INSERT INTO table_source (group_id, data, created) 
    VALUES (3, 'data', NOW()); --< Does not lock

INSERT INTO table_source (group_id, data, created) 
    VALUES (1, 'data', NOW()); --< Does not lock

INSERT INTO table_source (group_id, data, created) 
    VALUES (3, 'data', '2014-01-01'); --< Does lock

有人可以解释一下为什么会发生这种情况(我想它与间隙锁有关)?有没有办法避免这种情况(我仍然希望保持REPEATABLE READ隔离级别?)

1 个答案:

答案 0 :(得分:4)

这是对的。正在读取的表中的行使用共享锁进行锁定(SELECT隐式LOCK IN SHARE MODE)。没有办法避免这种情况。这是您要求系统执行的操作:复制符合条件的所有行。确保这一事实的唯一方法实际上是所有符合条件的行,并且该列表在执行该语句期间或之后不会立即更改,就是锁定行。

作为对INSERTgroup_id = 2

无法WHERE group_id = 3 AND created < '2014-01-04'的原因的澄清

这与您在KEY group_id_created (group_id, created)上专门针对group_id = 3 AND created < '2014-01-04'的查询有关。为了搜索匹配(3, '2014-01-14')的所有行,索引将从第一行开始向后遍历超过该条件的上限,即created并继续直到找到与该行不匹配的行条件,因为group_id < 3没有下限,所以group_id = 2当然是group_id = 2的第一行。

这意味着created遇到的第一行已锁定,这将是具有最大INSERT值的行。这将使(2, MAX(created))无法进入(3, MIN(created))和{{1}}之间的“差距”(当然不是正确的SQL,只是伪SQL),尽管这不是“缺口锁定”具体