【问题标题】:When I INSERT multiple rows into a MySQL table, will the ids be increment by 1 everytime?当我在 MySQL 表中插入多行时,ID 每次都会增加 1 吗?
【发布时间】:2023-03-14 17:20:01
【问题描述】:

如果我有如下查询:

INSERT INTO table (col1,col2,col3) VALUES
('col1_value_1', 'col2_value_1', 'col3_value_1'),
('col1_value_2', 'col2_value_2', 'col3_value_2'),
('col1_value_3', 'col2_value_3', 'col3_value_3');

假设我有一个表,其中最后一个 id PRIMARY_KEY AUTO_INCREMENT 值是 56,那么这个插入查询是否总是会创建 3 个 ID 为 57, 58, 59 的记录。这个操作是原子的吗?

或者,如果另一个查询写入同一个表,ids 是否不能始终递增 1?

感谢关注!

编辑:请阅读以下内容,因为我可能不太清楚。

当然AUTO_INCREMENT 安全地递增一,我知道。

重点是:

假设我有一个名为table 的表格:

 ___________________________________
| id | col1       | col2             |
-------------------------------------
|  1 | "some val" | "some other val" |
|  2 | "some val" | "some other val" |
|  3 | "some val" | "some other val" |
|  4 | "some val" | "some other val" |
|  5 | "some val" | "some other val" |
|  6 | "some val" | "some other val" |
|____________________________________|

如果我知道运行查询:

INSERT INTO table (col1,col2) VALUES
('some val', 'some other val'),
('some val', 'some other val'),
('some val', 'some other val')

我最终会得到下表:

 ___________________________________
| id | col1       | col2             |
-------------------------------------
|  1 | "some val" | "some other val" |
|  2 | "some val" | "some other val" |
|  3 | "some val" | "some other val" |
|  4 | "some val" | "some other val" |
|  5 | "some val" | "some other val" |
|  6 | "some val" | "some other val" |
|  7 | "some val" | "some other val" |
|  8 | "some val" | "some other val" |
|  9 | "some val" | "some other val" |
|____________________________________|

这里没什么好说的。但如果我和另一个人同时运行相同的查询,这些查询是原子吗?这意味着我们将总是最终得到:

1)

 ___________________________________
| id | col1       | col2             |
-------------------------------------
|  1 | "some val" | "some other val" |
|  2 | "some val" | "some other val" |
|  3 | "some val" | "some other val" |
|  4 | "some val" | "some other val" |
|  5 | "some val" | "some other val" |
|  6 | "some val" | "some other val" |
|  7 | "some val" | "some other val" |<-- My 1st inserted record
|  8 | "some val" | "some other val" |<-- My 2nd inserted record
|  9 | "some val" | "some other val" |<-- My 3rd inserted record
| 10 | "some val" | "some other val" |<-- Another guy's 1st inserted record
| 11 | "some val" | "some other val" |<-- Another guy's 2nd inserted record
| 12 | "some val" | "some other val" |<-- Another guy's 3rd inserted record
|____________________________________|

或与:

2)

 ___________________________________
| id | col1       | col2             |
-------------------------------------
|  1 | "some val" | "some other val" |
|  2 | "some val" | "some other val" |
|  3 | "some val" | "some other val" |
|  4 | "some val" | "some other val" |
|  5 | "some val" | "some other val" |
|  6 | "some val" | "some other val" |
|  7 | "some val" | "some other val" |<-- Another guy's 1st inserted record
|  8 | "some val" | "some other val" |<-- Another guy's 2nd inserted record
|  9 | "some val" | "some other val" |<-- Another guy's 3rd inserted record
| 10 | "some val" | "some other val" |<-- My 1st inserted record
| 11 | "some val" | "some other val" |<-- My 2nd inserted record
| 12 | "some val" | "some other val" |<-- My 3rd inserted record
|____________________________________|

取决于两个 MySQL 的哪个查询先调度。

或者是否也会出现以下异常?:

3)

 ___________________________________
| id | col1       | col2             |
-------------------------------------
|  1 | "some val" | "some other val" |
|  2 | "some val" | "some other val" |
|  3 | "some val" | "some other val" |
|  4 | "some val" | "some other val" |
|  5 | "some val" | "some other val" |
|  6 | "some val" | "some other val" |
|  7 | "some val" | "some other val" |<-- My 1st inserted record
|  8 | "some val" | "some other val" |<-- My 2nd inserted record
|  9 | "some val" | "some other val" |<-- Another guy's 1st inserted record - WTF???
| 10 | "some val" | "some other val" |<-- My 3rd inserted record 
| 11 | "some val" | "some other val" |<-- Another guy's 2nd inserted record
| 12 | "some val" | "some other val" |<-- Another guy's 3rd inserted record
|____________________________________|

或者是这样的:

4)

 ___________________________________
| id | col1       | col2             |
-------------------------------------
|  1 | "some val" | "some other val" |
|  2 | "some val" | "some other val" |
|  3 | "some val" | "some other val" |
|  4 | "some val" | "some other val" |
|  5 | "some val" | "some other val" |
|  6 | "some val" | "some other val" |
|  7 | "some val" | "some other val" |<-- Another guy's 1st inserted record
|  8 | "some val" | "some other val" |<-- My 1st inserted record - WTF???
|  9 | "some val" | "some other val" |<-- Another guy's 2nd inserted record
| 10 | "some val" | "some other val" |<-- My 2nd inserted record - WTF^2???
| 11 | "some val" | "some other val" |<-- Another guy's 3rd inserted record
| 12 | "some val" | "some other val" |<-- My 3rd inserted record - WTF^3???
|____________________________________|

或任何其他组合!= 3)4)

我认为 1)2) 是原子的。是否总是保证我会总是1)2) 结束并且永远不会3)4) 或任何其他组合结束?如果是的话(我总是以1)2) 结尾),对于MyISAMInnoDB

如果我这样做 SELECT LAST_INSERT_ID(); 和例如我得到7,这是否自动意味着带有id 89 的行也是由我的查询而不是由其他人的查询插入的?

【问题讨论】:

  • 是的,id 会一直递增。
  • @JörnBuitink 请检查我的编辑。
  • LAST_INSERT_ID() 在连接的基础上运行。其他人和你不会在同一个连接中做事情。因此,您是孤立的,您将收到自己操作的最后一个 id。
  • @Mjh 是的,我知道我和其他用户将收到我们自己操作的最后一个 id,并且会有所不同。这不是重点。关键是:给定我的批量 INSERT 查询,其中插入例如10 条记录,我执行它然后我得到LAST_INSERT_ID()。如果例如LAST_INSERT_ID() = 71 总是意味着我添加的 10 条记录的 id 为 71727374757677 , 78, 79, 80?
  • 它没有必须,但因为它是批量插入,所以是的。

标签: mysql last-insert-id


【解决方案1】:

如果您在一个语句中插入值,场景 3 和 4 即使您正在使用它们也不会出现 innodb_autoinc_lock_mode = 2。来自文档 https://dev.mysql.com/doc/refman/5.7/en/innodb-auto-increment-handling.html

innodb_autoinc_lock_mode = 2 在这种锁定模式下,自动增量值保证在所有并发执行的“类似插入”的语句中是唯一的且单调递增的。

测试过

【讨论】:

  • 感谢您的洞察!这对 MyISAM 表是否也有效(没有出现场景 3 和 4 的事实)?那么innodb_autoinc_lock_mode = 2 标志的用途是什么?
  • 测试了我的 myisam 脚本,而场景脚本 3 和 4 没有出现。这是因为插入,即使它们有多个值,也是一个事务并且不会拆分为单个插入。关于 Innodb_autoinc_lock_mode = 2 来自文档 In this lock mode, no “INSERT-like” statements use the table-level AUTO-INC lock, and multiple statements can execute at the same time. This is the fastest and most scalable lock mode, but it is not safe when using statement-based replication or recovery scenarios when SQL statements are replayed from the binary log
  • 很抱歉,从文档中的解释中我没有理解这种锁定模式,请您进一步解释并举例说明吗?谢谢!
  • 如果使用innodb_autoinc_lock_mode = 2,数据库不等待,前面的INSERT完成,同时执行。 START INSERT1, START INSERT2, START INSERT3, END INSERT2, END INSERT3, END INSERT1 这工作得更快,但如果您的二进制日志使用语句,则可以完成,因为插入可以以不同的速度进行。从机上的主键可以接受不同的值。
  • 这个答案不正确。在交错模式下(innodb_autoinc_lock_mode = 2)。链接的 MySQL 文档在引用的段落之后明确写道: " 但是,由于多个语句可以同时生成数字(即,数字的分配在语句之间交错),为行生成的 值任何给定语句插入的内容可能不是连续的。”我在几个月前的回答中已经描述了所有这些。
【解决方案2】:

答案是:嗯,这取决于。

对于 myisam,答案是肯定的,因为 myisam 序列插入请求。

然而,对于 innodb,行为是可配置的,因为 mysql v5.1。在 v5.1 之前,InnoDB 的答案也是肯定的,之后取决于 innodb_autoinc_lock_mode 设置。请参阅InnoDB auto_increment configuration for details 上的 mysql 文档。

为了给你亮点,有3个innodb_autoinc_lock_mode设置:

  1. 传统 (0)
  2. 后续 (1) - 默认
  3. 交错 (2)

将 innodb_autoinc_lock_mode 设置为 0(“传统”)或 1(“连续”),任何给定生成的自动增量值 语句将是连续的,没有间隙,因为表级 AUTO-INC 锁一直保持到语句结束,并且只有一个 这样的语句可以一次执行。

将 innodb_autoinc_lock_mode 设置为 2(“interleaved”),可能会有 由“批量插入”生成的自动增量值中的间隙,但是 仅当同时执行“INSERT-like”语句时。

对于锁定模式 1 或 2,连续语句之间可能会出现间隙 因为对于批量插入自动增量值的确切数量 每个陈述所要求的可能不为人所知,并且高估是 可能。

如果事务已回滚,则 auto_increment 值可能会出现进一步的差距。批量插入只能作为一个整体回滚。

更新: 如上所述,如果您使用

,您将获得场景 1) 或 2)
  • myisam 表引擎
  • 或innodb pre mysql v5.1
  • 或带有 mysql v5.1 或更新版本的 innodb,innodb_autoinc_lock_mode 为 0 或 1

没有办法知道哪个先插入。

如果您使用

,您可能会得到场景 3) 或 4)
  • innodb 与 innodb_autoinc_lock_mode 2

同样,没有办法说明 mysql 如何以及为什么会混淆记录的顺序。

因此,如果您的问题与您使用批量插入插入 3 条记录并且 last_insert_id() 仅返回第一条插入记录的 auto_increment 值有关,并且您希望通过简单的添加来获取其他 2 条记录的 id是您可能需要根据使用的表引擎和mysql版本检查mysql的配置。

【讨论】:

  • 为了您的方便:您可以通过运行查询SHOW VARIABLES LIKE '%innodb_autoinc_lock_mode%'来检查您的设置是什么
  • 很好的解释!
【解决方案3】:

mysql 将多插入查询视为一个事务或一个查询,将插入所有行,如果失败则不会插入任何行,因此如果插入此查询:

INSERT INTO table (col1,col2,col3) VALUES
('col1_value_1', 'col2_value_1', 'col3_value_1'),
('col1_value_2', 'col2_value_2', 'col3_value_2'),
('col1_value_3', 'col2_value_3', 'col3_value_3');

mysql 将把它作为一个查询运行,如果你的 id 自动递增,它将使用你的 ids 57、58、59。 如果其他用户同时通过插入查询,如果其他用户查询比您的查询花费更多时间,则您的查询将花费 57,58,59 如果您的查询比其他用户花费更多时间,那么您的 ids 将启动从另一个用户查询端。因此,无论哪种情况,当 id 为自动增量时的多插入查询都将在查询中进行排序。

【讨论】:

  • 总是像你说的那样。 MyISAMInnoDB?
  • 我认为这适用于 InnoDB,但 MyIsam 会同时锁定插入,因此如果您开始运行查询。其他用户查询不会完成。
  • 所以,你所说的绝对总是适用于MyISAM,并且不会出现像我发布的那些异常。你确认了吗?
  • 我测试了我对 InnoDB 所说的内容,并从 InnoDB 中确定,我知道但不确认 MyISAM 锁定查询在我阅读 dba.stackexchange.com/questions/21075/… 的同时进行
【解决方案4】:

auto_increment并发环境中是安全的。它的工作是赋予独特的价值,无论您与多少人联系并在一张桌子上工作。您可以控制增量的偏移量,默认为1

现在这实际上意味着什么 - 这意味着表中写入的内容不必增加 1。这就是著名的“间隙”问题。

假设你和我同时给你的桌子写信。我写了记录10, 11, 12,你写了13, 14, 15。但是,可能发生了一些不好的事情(死锁或事务失败)并且我的结果没有保留 - 查询失败并且auto_increment 被花掉了。在这种情况下,您的记录 (13, 14, 15) 会写入磁盘,而我没有。

这是正常行为。您的表格不必包含递增 1 的数字。它将包含唯一数字,这是 auto_increment 的工作。

【讨论】:

  • @tonix - 你最终得到什么重要吗?我认为顺序或实际值无关紧要。您最终得到的结果取决于您的所有插入查询是否都在事务中执行。从技术上讲,您最终会得到示例的案例1。但是,准确的说法是您最终会得到具有唯一标识符的记录。出于好奇,您为什么要询问实际的插入顺序?
  • 好吧PHPPDO 类有一个方法lastInsertId()。此方法返回 MySQL 的 LAST_INSERT_ID() 结果。当我使用一个批量INSERT 查询插入多行时,之后我调用 PDO 的lastInsertId()。它将返回我的批量INSERT 中第一个插入行的id。现在,我想以某种方式知道我在该批量查询中的第一行之后添加的下一个n 行的ids,但PDO 没有提供这样的lastInsertIds() 方法。假设我知道我添加了 3 行并且 lastInsertId() = 7,我可以安全地取 (7..9) 之间的范围,即 `7、8、9?
  • 对,既然您使用的是PDO,请问您为什么不使用准备好的语句而不是批量插入?这比使用批量插入要明智得多,而且您实际上可以通过这种方式检索所有生成的 id。使用批量插入,您无法获得所需的东西 - 存在满足您需求的东西。
  • 好吧,我可以将准备好的语句与批量插入一起使用。您是说我不能将准备好的语句与批量插入一起使用吗?如果是,那么我可以向你保证,你错了。
  • 我没有声明您不能将准备好的语句与批量插入一起使用。这毫无意义,您所追求的是每个查询的 insert_id 。批量插入不会让你拥有它。还有各种与批量插入相关的问题——例如,您很容易超过max_allowed_packet 的值。我在您的使用场景中看不到批量插入的意义,看来在循环中执行的准备好的语句可以满足您的需求。将其包装在事务中也会为您提供高插入率。
【解决方案5】:

如果你定义任何列的主键 aut_increment 那么它会自动从 1 开始增加值, 您不需要在插入查询中定义此列,它会自动在主键列中插入递增值。

【讨论】:

    猜你喜欢
    • 2019-10-21
    • 2023-03-11
    • 2018-11-04
    • 2019-12-04
    • 1970-01-01
    • 2014-10-25
    • 2013-10-31
    • 2013-09-17
    • 2023-03-29
    相关资源
    最近更新 更多