防止 InnoDB 在 DUPLICATE KEY 上自动递增
Prevent InnoDB auto increment ON DUPLICATE KEY
我目前遇到主键 ID
设置为 auto increment
的问题。它不断递增 ON DUPLICATE KEY
.
例如:
ID | field1 | field2
1 | user | value
5 | secondUser | value
86 | thirdUser | value
从上面的描述中,您会注意到我在其中有 3 个输入 table,但由于每次更新时自动递增,第三个输入的 ID 为 86。
有没有办法避免这种情况?
下面是我的 mySQL 查询:
INSERT INTO table ( field1, field2 ) VALUES (:value1, :value2)
ON DUPLICATE KEY
UPDATE field1 = :value1, field2 = :value2
这是我的 table 的样子;
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 ;
I am currently having problems with a primary key ID
which is set to
auto increment
. It keeps incrementing ON DUPLICATE KEY
我们中的一个人一定是误解了这个问题,或者你在歪曲它。 ON DUPLICATE KEY UPDATE
从不创建新行,因此它不能递增。来自 the docs:
If you specify ON DUPLICATE KEY UPDATE, and a row is inserted that
would cause a duplicate value in a UNIQUE index or PRIMARY KEY, MySQL
performs an UPDATE of the old row.
现在可能是插入时自增,没有发现重复键。如果我假设这就是正在发生的事情,我的问题是:为什么这是个问题?
如果您绝对想控制主键的值,请更改您的 table 结构以删除 auto-increment
标志,但将其保留为必需的非空字段。它会迫使你自己提供密钥,但我敢打赌这会让你更头疼。
不过我真的很好奇:为什么你需要填补 ID
值中的所有漏洞?
您可以将 innodb_autoinc_lock_mode
配置选项设置为 "0"
用于 "traditional" 自动增量锁定模式,这保证所有 INSERT
语句将为 AUTO_INCREMENT
列。
也就是说,您不应该依赖应用程序中连续的自动递增 ID。他们的目的是提供唯一的标识符。
此行为在下面使用 innodb_autoinc_lock_mode
= 1 (“consecutive” lock mode). Please also reference the fine manual page entitled AUTO_INCREMENT Handling in InnoDB 的默认设置很容易看出。更改此值将降低并发性和性能,因为“传统”锁定模式设置为 0,因为它使用 table 级别的 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 在打印输出表中的空白(以及说 "Why do we have gaps" 的老板),你关闭了。
在插入重复键更新 (IODKU
) 的情况下,它假设有 1 个新行并为其分配一个插槽。请记住,并发性,以及您的同行执行相同的操作,可能并发数百个。当 IODKU 变成 Update
时,好吧,您的连接和其他任何人都将使用 id=2 的已放弃且从未插入的行。
示例 2:
在我的 中,Insert ... Select From
期间也会发生同样的情况。由于报告计数、最小值、最大值,我特意在其中使用 MyISAM
,否则范围间隙怪癖将分配而不是全部填满。这些数字看起来很奇怪,因为该答案处理的是实际数字。因此,较旧的引擎 (MyISAM
) 可以很好地实现紧密的无间隙。请注意,在那个答案中,我试图做一些快速和安全的事情,并且 table 可以在事后用 ALTER TABLE
转换为 INNODB
。如果我一开始就在 INNODB
中完成了那个例子,就会有很多空白(在默认模式下)。如果我使用 INNODB
,Insert ... Select From
会 在答案中产生空白的原因是由于计数的不确定性,引擎选择的安全机制(不确定的)范围分配。 INNODB
引擎自然知道该操作,知道必须创建一个 AUTO_INCREMENT
id 的安全池,具有并发性(其他用户要考虑),并且差距蓬勃发展。这是事实。使用 INNODB
引擎尝试示例 2,看看您对 min、max 和 count 想出了什么。最大值不等于计数。
示例 3 和 4:
有多种情况导致 INNODB
Percona 网站上记录了差距,因为他们偶然发现了更多差距并记录下来。例如,由于在此 1452 Error image. Or a Primary Key error in this 1062 Error image.
中看到的外键约束,它发生在插入失败期间
请记住,INNODB
差距是系统性能和安全引擎的副作用。为了更严格的 id 范围,人们真的想关闭它(性能、更高的用户统计、更高的并发性、缺少 table 锁)吗?无论如何,在删除上有漏洞的范围。我建议不要用于我的实现,默认的 Performance 就很好。
我回答了here:
要解决自动递增问题,请在 insert/on 复制更新部分之前使用以下代码并一起执行它们:
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";
您可以从
更改您的查询
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 插入新数据(不是自动递增)
- 如果在唯一字段中发现重复条目,请忽略它
- 否则正常插入新数据
(但此方法不支持在发现重复条目时更新字段)
我目前遇到主键 ID
设置为 auto increment
的问题。它不断递增 ON DUPLICATE KEY
.
例如:
ID | field1 | field2
1 | user | value
5 | secondUser | value
86 | thirdUser | value
从上面的描述中,您会注意到我在其中有 3 个输入 table,但由于每次更新时自动递增,第三个输入的 ID 为 86。
有没有办法避免这种情况?
下面是我的 mySQL 查询:
INSERT INTO table ( field1, field2 ) VALUES (:value1, :value2)
ON DUPLICATE KEY
UPDATE field1 = :value1, field2 = :value2
这是我的 table 的样子;
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 ;
I am currently having problems with a primary key
ID
which is set toauto increment
. It keeps incrementingON DUPLICATE KEY
我们中的一个人一定是误解了这个问题,或者你在歪曲它。 ON DUPLICATE KEY UPDATE
从不创建新行,因此它不能递增。来自 the docs:
If you specify ON DUPLICATE KEY UPDATE, and a row is inserted that would cause a duplicate value in a UNIQUE index or PRIMARY KEY, MySQL performs an UPDATE of the old row.
现在可能是插入时自增,没有发现重复键。如果我假设这就是正在发生的事情,我的问题是:为什么这是个问题?
如果您绝对想控制主键的值,请更改您的 table 结构以删除 auto-increment
标志,但将其保留为必需的非空字段。它会迫使你自己提供密钥,但我敢打赌这会让你更头疼。
不过我真的很好奇:为什么你需要填补 ID
值中的所有漏洞?
您可以将 innodb_autoinc_lock_mode
配置选项设置为 "0"
用于 "traditional" 自动增量锁定模式,这保证所有 INSERT
语句将为 AUTO_INCREMENT
列。
也就是说,您不应该依赖应用程序中连续的自动递增 ID。他们的目的是提供唯一的标识符。
此行为在下面使用 innodb_autoinc_lock_mode
= 1 (“consecutive” lock mode). Please also reference the fine manual page entitled AUTO_INCREMENT Handling in InnoDB 的默认设置很容易看出。更改此值将降低并发性和性能,因为“传统”锁定模式设置为 0,因为它使用 table 级别的 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 在打印输出表中的空白(以及说 "Why do we have gaps" 的老板),你关闭了。
在插入重复键更新 (IODKU
) 的情况下,它假设有 1 个新行并为其分配一个插槽。请记住,并发性,以及您的同行执行相同的操作,可能并发数百个。当 IODKU 变成 Update
时,好吧,您的连接和其他任何人都将使用 id=2 的已放弃且从未插入的行。
示例 2:
在我的 Insert ... Select From
期间也会发生同样的情况。由于报告计数、最小值、最大值,我特意在其中使用 MyISAM
,否则范围间隙怪癖将分配而不是全部填满。这些数字看起来很奇怪,因为该答案处理的是实际数字。因此,较旧的引擎 (MyISAM
) 可以很好地实现紧密的无间隙。请注意,在那个答案中,我试图做一些快速和安全的事情,并且 table 可以在事后用 ALTER TABLE
转换为 INNODB
。如果我一开始就在 INNODB
中完成了那个例子,就会有很多空白(在默认模式下)。如果我使用 INNODB
,Insert ... Select From
会 在答案中产生空白的原因是由于计数的不确定性,引擎选择的安全机制(不确定的)范围分配。 INNODB
引擎自然知道该操作,知道必须创建一个 AUTO_INCREMENT
id 的安全池,具有并发性(其他用户要考虑),并且差距蓬勃发展。这是事实。使用 INNODB
引擎尝试示例 2,看看您对 min、max 和 count 想出了什么。最大值不等于计数。
示例 3 和 4:
有多种情况导致 INNODB
Percona 网站上记录了差距,因为他们偶然发现了更多差距并记录下来。例如,由于在此 1452 Error image. Or a Primary Key error in this 1062 Error image.
请记住,INNODB
差距是系统性能和安全引擎的副作用。为了更严格的 id 范围,人们真的想关闭它(性能、更高的用户统计、更高的并发性、缺少 table 锁)吗?无论如何,在删除上有漏洞的范围。我建议不要用于我的实现,默认的 Performance 就很好。
我回答了here: 要解决自动递增问题,请在 insert/on 复制更新部分之前使用以下代码并一起执行它们:
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";
您可以从
更改您的查询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 插入新数据(不是自动递增)
- 如果在唯一字段中发现重复条目,请忽略它
- 否则正常插入新数据
(但此方法不支持在发现重复条目时更新字段)