为什么 MySQL 自动增量会在插入失败时增加?

发布于 2024-11-29 02:01:03 字数 539 浏览 1 评论 0原文

一位同事刚刚让我意识到 MySQL 的一个非常奇怪的行为。

假设您有一个带有 auto_increment 字段的表和另一个设置为唯一的字段(例如用户名字段)。当尝试插入具有表中已存在的用户名的行时,插入会失败,如预期的那样。然而,当您在多次尝试失败后插入有效的新条目时,可以看到 auto_increment 值会增加。

例如,当我们的最后一个条目看起来像这样...

ID: 10
Username: myname

...并且我们在下一次插入时尝试具有相同用户名值的五个新条目时,我们将创建一个新行,如下所示:

ID: 16
Username: mynewname

虽然这本身并不是一个大问题通过用失败的插入请求淹没表来杀死表似乎是一种非常愚蠢的攻击方式,正如 MySQL 参考手册所述:

“如果 [...] 值大于可以存储在指定整数类型中的最大整数,则未定义自动增量机制的行为。”

这是预期的行为吗?

A co-worker just made me aware of a very strange MySQL behavior.

Assuming you have a table with an auto_increment field and another field that is set to unique (e.g. a username-field). When trying to insert a row with a username thats already in the table the insert fails, as expected. Yet the auto_increment value is increased as can be seen when you insert a valid new entry after several failed attempts.

For example, when our last entry looks like this...

ID: 10
Username: myname

...and we try five new entries with the same username value on our next insert we will have created a new row like so:

ID: 16
Username: mynewname

While this is not a big problem in itself it seems like a very silly attack vector to kill a table by flooding it with failed insert requests, as the MySQL Reference Manual states:

"The behavior of the auto-increment mechanism is not defined if [...] the value becomes bigger than the maximum integer that can be stored in the specified integer type."

Is this expected behavior?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

长伴 2024-12-06 02:01:03

InnoDB 是一个事务引擎。

这意味着在以下场景中:

  1. Session A 插入记录 1
  2. Session B 插入记录 2
  3. Session A 回滚

,可能存在间隙,或者会话 B 会锁定,直到会话 A 提交或回滚。

InnoDB 设计者(与大多数其他事务引擎设计者一样)选择允许间隙。

来自文档< /a>:

访问自动递增计数器时,InnoDB 使用特殊的表级 AUTO-INC 锁,该锁将保留到当前 SQL< 的末尾/code> 语句,不至于事务结束。引入特殊的锁释放策略是为了提高向包含 AUTO_INCRMENT 列的表中插入的并发性

InnoDB 只要服务器运行,就会使用内存中的自动递增计数器。当服务器停止并重新启动时,InnoDB 会重新初始化每个表的计数器,以便对表进行第一次 INSERT,如前所述。

如果您担心 id 列环绕,请将其设为 BIGINT(8 字节长)。

InnoDB is a transactional engine.

This means that in the following scenario:

  1. Session A inserts record 1
  2. Session B inserts record 2
  3. Session A rolls back

, there is either a possibility of a gap or session B would lock until the session A committed or rolled back.

InnoDB designers (as most of the other transactional engine designers) chose to allow gaps.

From the documentation:

When accessing the auto-increment counter, InnoDB uses a special table-level AUTO-INC lock that it keeps to the end of the current SQL statement, not to the end of the transaction. The special lock release strategy was introduced to improve concurrency for inserts into a table containing an AUTO_INCREMENT column

InnoDB uses the in-memory auto-increment counter as long as the server runs. When the server is stopped and restarted, InnoDB reinitializes the counter for each table for the first INSERT to the table, as described earlier.

If you are afraid of the id column wrapping around, make it BIGINT (8-byte long).

向日葵 2024-12-06 02:01:03

在不知道确切的内部结构的情况下,我会说是的,自动增量应该允许跳过值来导致插入失败。假设您正在进行银行交易,或其他整个交易和多条记录要么全有要么全无的交易。如果您尝试插入,获取一个 ID,然后用该事务 ID 标记所有后续详细信息并插入详细记录,则需要确保合格的唯一性。如果您有多人攻击数据库,他们也需要确保获得自己的事务 ID,以免在提交事务时与您的事务 ID 发生冲突。如果第一个事务出现故障,不会造成任何损害,下游也不会出现悬空元素。

Without knowing the exact internals, I would say yes, the auto-increment SHOULD allow for skipped values do to failure inserts. Lets say you are doing a banking transaction, or other where the entire transaction and multiple records go as an all-or-nothing. If you try your insert, get an ID, then stamp all subsequent details with that transaction ID and insert the detail records, you need to ensure your qualified uniqueness. If you have multiple people slamming the database, they too will need to ensure they get their own transaction ID as to not conflict with yours when their transaction gets committed. If something fails on the first transaction, no harm done, and no dangling elements downstream.

美人如玉 2024-12-06 02:01:03

老帖子,
但这可能会帮助人们,
您可能必须将 innodb_autoinc_lock_mode 设置为 <强>0或2。

采用数值的系统变量可以在命令行上指定为 --var_name=value 或在选项文件中指定为 var_name=value

命令行参数格式:

--innodb-autoinc-lock-mode=0 

OR
打开 mysql.ini 并添加以下行:

innodb_autoinc_lock_mode=0

Old post,
but this may help people,
You may have to set innodb_autoinc_lock_mode to 0 or 2.

System variables that take a numeric value can be specified as --var_name=value on the command line or as var_name=value in option files.

Command-Line parameter format:

--innodb-autoinc-lock-mode=0 

OR
Open your mysql.ini and add following line :

innodb_autoinc_lock_mode=0
娇女薄笑 2024-12-06 02:01:03

我知道这是一篇旧文章,但由于我也找不到正确的答案,所以我实际上找到了一种方法来做到这一点。您必须将查询包含在 if 语句中。它通常是插入查询或插入和重复查询,这会扰乱组织的自动增量顺序,因此对于常规插入,请使用:

$check_email_address = //select query here\\

if ( $check_email_address == false ) {
    your query inside of here
}

而不是 INSERT AND ON DUPLICATE 使用 UPDATE SET WHERE QUERY 在 if 语句之内或之外并不重要,REPLACE INTO QUERY 似乎也有效

I know that this is an old article but since I also couldn't find the right answer, I actually found a way to do this. You have to wrap your query within an if statement. Its usually insert query or insert and on duplicate querys that mess up the organized auto increment order so for regular inserts use:

$check_email_address = //select query here\\

if ( $check_email_address == false ) {
    your query inside of here
}

and instead of INSERT AND ON DUPLICATE use a UPDATE SET WHERE QUERY in or outside an if statement doesn't matter and a REPLACE INTO QUERY also does seem to work

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文