防止InnoDB自动递增ON DUPLICATE KEY

时间:2016-07-13 09:01:08

标签: php mysql auto-increment on-duplicate-key

我目前遇到主键ID设置为auto increment的问题。它不断递增ON DUPLICATE KEY

例如:

ID | field1     | field2

1  | user       | value

5  | secondUser | value

86 | thirdUser  | value

从上面的描述中,您会注意到我在该表中有3个输入但由于每次更新时自动递增,因此第三个输入的ID为86。

无论如何都要避免这种情况吗?

这是我的mySQL查询的样子:

INSERT INTO table ( field1, field2 ) VALUES (:value1, :value2)
            ON DUPLICATE KEY
            UPDATE field1 = :value1, field2 = :value2 

这就是我的桌子的样子;

CREATE TABLE IF NOT EXISTS `table` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `field1` varchar(200) NOT NULL,
  `field2` varchar(255) NOT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `field1` (`field1`),
  KEY `id` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;

5 个答案:

答案 0 :(得分:5)

您可以将innodb_autoinc_lock_mode配置选项设置为"0"以用于“传统”自动增量锁定模式,这样可以保证所有INSERT语句都为AUTO_INCREMENT分配连续值列。

也就是说,您不应该依赖应用程序中连续的自动增量ID。他们的目的是提供唯一标识符。

答案 1 :(得分:1)

下面使用innodb_autoinc_lock_mode = 1(“连续”锁定模式)的默认设置很容易看到此行为。另请参阅标题为AUTO_INCREMENT Handling in InnoDB的精细手册页。对于“Tranditional”锁定模式,更改此值将降低并发性和性能,因为它使用表级别的AUTO-INC锁定。

也就是说,以下是默认设置= 1。

我即将向您展示四个示例,说明创建差距的难易程度。

示例1:

create table x
(   id int auto_increment primary key,
    someOtherUniqueKey varchar(50) not null,
    touched int not null,
    unique key(someOtherUniqueKey)
);
insert x(touched,someOtherUniqueKey) values (1,'dog') on duplicate key update touched=touched+1;
insert x(touched,someOtherUniqueKey) values (1,'dog') on duplicate key update touched=touched+1;
insert x(touched,someOtherUniqueKey) values (1,'cat') on duplicate key update touched=touched+1;

select * from x; 
+----+--------------------+---------+
| id | someOtherUniqueKey | touched |
+----+--------------------+---------+
|  1 | dog                |       2 |
|  3 | cat                |       1 |
+----+--------------------+---------+

Gap(跳过id = 2)是由于INNODB引擎的一些操作和怪癖以及紧张抽搐之一造成的。在其默认的高性能并发模式下,它为发送给它的各种查询执行范围间隙分配。最好有理由改变这个设置,因为这样做会影响性能。 MySQL的后期版本会为您提供各种各样的东西,并且由于Hyper Focusing打印输出表中的空白(以及“我们为什么会有空白”的老板)而关闭。

如果在重复密钥更新(IODKU)上插入,则假设有1个新行并为其分配一个插槽。记住,并发性和你的同行做同样的操作,可能是数百个并发。当IODKU变成Update时,那么,对于你的连接和其他任何人,使用那个被遗弃且从未插入id = 2的行。

示例2:

Insert ... Select From期间发生同样的事情,如我This Answer所见。在其中我故意使用MyISAM由于报告计数,最小值,最大值,否则范围间隙怪癖将分配而不是全部填充。当答案涉及实际数字时,数字看起来很奇怪。因此,较旧的引擎(MyISAM)可以很好地用于紧密的无间隙。请注意,在该答案中,我尝试快速安全地执行某项操作,并且事后可以使用INNODB将表格转换为ALTER TABLE。如果我在INNODB开始做那个例子,就会有很多空白(在默认模式下)。 Insert ... Select From 的原因在我使用INNODB的答案中造成的差距是由于计数的不确定性,引擎选择安全的机制(不确定)范围分配。 INNODB引擎自然地知道操作,知道必须创建AUTO_INCREMENT id的安全池,并发(其他用户要考虑),并且差距蓬勃发展。这是事实。尝试使用INNODB引擎的示例2,看看你为min,max和count提出了什么。 Max不等于数。

示例3和4:

有各种各样的情况导致Percona网站上记录INNODB差距,因为他们偶然发现并记录下来。例如,由于在此1452 Error image中看到外键约束,它会在失败的插入期间发生。或此1062 Error image中的主键错误。

请记住,INNODB差距是系统性能和安全引擎的副作用。为了更严格的id范围,是否真的想要关闭(性能,更高的用户满意度,更高的并发性,缺少表锁)?无论如何都有删除漏洞的范围。我建议不要使用我的实现,并且使用Performance的默认设置就好了。

答案 2 :(得分:0)

  

我目前遇到设置为主键ID的问题   auto increment。它不断递增ON DUPLICATE KEY

我们中的一个人必须误解这个问题,否则你就会歪曲它。 ON DUPLICATE KEY UPDATE永远不会创建新行,因此无法递增。来自the docs

  

如果指定ON DUPLICATE KEY UPDATE,则插入一行   会在UNIQUE索引或PRIMARY KEY,MySQL中导致重复值   执行旧行的更新。

现在可能会出现插入时发生自动增量并且找到没有重复键的情况。如果我认为这就是正在发生的事情,我的问题是:为什么这是一个问题?

如果您绝对想要控制主键的值,请更改表结构以删除auto-increment标志,但将其保留为必需的非空字段。它会迫使你自己提供钥匙,但我敢打赌,这对你来说会更加令人头痛。

我真的很好奇:为什么你需要插入ID值中的所有漏洞?

答案 3 :(得分:0)

我回答了here: 要解决自动递增问题,请在插入/复制重复更新部分之前使用以下代码,并一起执行所有操作:

    SET @NEW_AI = (SELECT MAX(`the_id`)+1 FROM `table_blah`);
    SET @ALTER_SQL = CONCAT('ALTER TABLE `table_blah` AUTO_INCREMENT =', @NEW_AI);
    PREPARE NEWSQL FROM @ALTER_SQL;
    EXECUTE NEWSQL; 

一句话,它应该像下面这样:

    SET @NEW_AI = (SELECT MAX(`the_id`)+1 FROM `table_blah`);
    SET @ALTER_SQL = CONCAT('ALTER TABLE `table_blah` AUTO_INCREMENT =', @NEW_AI);
    PREPARE NEWSQL FROM @ALTER_SQL;
    EXECUTE NEWSQL; 
    INSERT INTO `table_blah` (`the_col`) VALUES("the_value")
    ON DUPLICATE KEY UPDATE `the_col` = "the_value";

答案 4 :(得分:0)

您可以从以下位置更改查询

INSERT INTO table ( f1, f2 ) VALUES (:v1, :v2) ON DUPLICATE KEY UPDATE f1 = :v1, f2 = :v2

insert ignore into table select (select max(id)+1 from table), :v1, :v2 ;

这将尝试

  • 插入具有最后一个未使用的ID(不是自动递增)的新数据
  • 如果在唯一字段中发现重复条目​​,请忽略它
  • 否则将正常插入新数据

    (但是如果找到重复的条目,此方法不支持更新字段)