【问题标题】:MySQL: Why does my INSERT statement skip 56 numbers when auto-incrementing the id?MySQL:为什么我的 INSERT 语句在自动递增 id 时会跳过 56 个数字?
【发布时间】:2020-08-28 12:29:34
【问题描述】:

在向我的 SQL 课程的学生演示 INSERT 语句时,我们在 MySQL 8.0 中发现了一些奇怪的行为。请帮助我们了解正在发生的事情。 (不需要解决方法,因为我们知道有一些解决方法,这是用于学习,而不是用于生产。谢谢)

我们正在创建一个新数据库并从著名的Sakila 示例数据库中复制一些行,如下所示:

CREATE DATABASE simpsons;

USE simpsons;

CREATE TABLE `character` (
    character_id smallint unsigned NOT NULL AUTO_INCREMENT,
    first_name VARCHAR(20) NOT NULL,
    last_name VARCHAR(20),
    shoe_size INT,
    PRIMARY KEY (character_id));

INSERT INTO `character` 
        (first_name, last_name)
    SELECT 
        first_name, last_name 
    FROM 
        sakila.actor;

当我们这样做并SELECT * FROM ``character`` 时,我们会看到来自sakila.actor 的所有200 条记录都已正确复制到新的character 表中。

最后一行的 200 自动递增 PK 的值为 character_id。输出窗口显示上述任何命令都没有错误。

然后,当我们立即手动添加一条记录时:

INSERT INTO `character`
    (first_name, last_name, shoe_size)
VALUES
    ('Bart', 'Simpson', 35);

很奇怪,我们发现这条记录的值256 是它的character_id,而不是201

尽管运行SHOW VARIABLES LIKE 'auto_inc%'; 表明auto_increment_incrementauto_increment_offset 都设置为1,但这是事实。

我们想了解为什么 MySQL 会跳过 56 个数字?


请注意,这个问题与 MySQL InnoDB auto_increment value increases by 2 instead of 1. Virus?MySQL autoincrement column jumps by 10- why? 不同,因为 auto_incerement_increment 是 1,在我们(易于重现)的场景中没有 DELETE 操作,我们每个人都是我们预期的唯一用户数据库。另外,对于这个问题的答案,对于实际发生的事情,没有一个是决定性的。最后,请参阅@Postman 的精彩答案,其中引用了上述问题的任何答案中未提及的根本原因。谢谢

【问题讨论】:

标签: mysql auto-increment


【解决方案1】:

此行为与"bulk inserts"innodb_autoinc_lock_mode 设置有关。

据我了解(文档对此不太清楚),当您使用 INSERT INTO ... SELECT 语句时,MySQL 无法知道在运行查询之前实际插入了多少行,但使用 innodb_autoinc_lock_mode=1(连续)或 2(交错)时,必须保留新的 AUTO_INCREMENT 值。根据我的观察,它保留了一组 AUTO_INCREMENT 数字,其中计数是 2 的幂(无法证实这一点,只能猜测)。请参见以下示例:

CREATE TABLE sourceTable(
    id INT AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(20)
);

CREATE TABLE targetTable(
    id INT AUTO_INCREMENT PRIMARY KEY,
    original VARCHAR(30)
);

INSERT INTO sourceTable(name) VALUES ('one');
INSERT INTO sourceTable(name) VALUES ('two');
INSERT INTO sourceTable(name) VALUES ('three');
INSERT INTO sourceTable(name) VALUES ('four');
INSERT INTO sourceTable(name) VALUES ('five');

INSERT INTO targetTable(original) SELECT name FROM sourceTable;

INSERT INTO targetTable(original) VALUES ('manual');

SELECT * FROM targetTable;

这将生成以下输出:

+----+----------+
| id | original |
+----+----------+
|  1 | one      |
|  2 | two      |
|  3 | three    |
|  4 | four     |
|  5 | five     |
|  8 | manual   |
+----+----------+

从源表中插入 5 行时,它会保留下 8 个可能的 AUTO_INCREMENT 值,因为这是大于 5 的 2 的最接近的幂。但是,由于您只插入 5 行,因此它将仅使用其中的 5 个。

在您的情况下,您要插入 200 行,因此大于 200 的 2 的最接近幂为 256。因此,您有 56 个缺失 AUTO_INCREMENT 值的“间隙”,下一个条目的 ID 为 256。

【讨论】:

  • 现在我很好奇 - MySQL 如何知道使用 2 的哪个幂?
  • @urig Unknown(请记住,我的假设只是推测)。也许这是某种迭代过程,在读取源数据时保留了更大的插槽范围。所以它可能会在开始时保留1,而不是一个额外的1,然后是2,然后是4,然后是8,......直到它保留最后一个插槽范围128(到已经使用128 IDs) 来填充原始200 行中的剩余行。但同样,只是猜测。
  • 从阅读文章 (dev.mysql.com/doc/refman/8.0/en/…) 开始,我认为 auto-inc 值的保留只在执行开始时完成了一次?为什么它是迭代完成的?为什么增量会受到限制?我希望知道代码的人会大声笑
  • 查看此链接了解更多信息:stackoverflow.com/questions/62969236/…
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-04-03
  • 2014-06-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多