为什么MySQL自动增量会因失败的插入而增加?

时间:2010-05-07 10:44:24

标签: mysql innodb auto-increment

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

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

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

ID: 10
Username: myname

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

ID: 16
Username: mynewname

虽然这本身并不是一个大问题,但似乎是一个非常愚蠢的攻击向量,通过使用失败的插入请求充斥它来杀死一个表,正如MySQL参考手册所述:

  

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

这是预期的行为吗?

4 个答案:

答案 0 :(得分:31)

InnoDB是一个交易引擎。

这意味着在以下场景中:

  1. Session A插入记录1
  2. Session B插入记录2
  3. Session A回滚
  4. ,在session B提交或回滚之前,可能存在间隙或session A会锁定。

    InnoDB设计师(正如大多数其他交易引擎设计师一样)选择允许差距。

    来自documentation

      

    访问自动增量计数器时,InnoDB使用一个特殊的表级AUTO-INC锁,它保持在当前SQL语句的末尾,而不是结束时交易。引入了特殊的锁定释放策略,以提高插入包含AUTO_INCREMENT

    的表的并发性      

    ...

         只要服务器运行,

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

    如果您害怕id列缠绕,请将其设为BIGINT(8字节长)。

答案 1 :(得分:5)

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

答案 2 :(得分:2)

老帖子, 但这可能对人有帮助, 您可能需要将innodb_autoinc_lock_mode设置为 0或2

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

命令行参数格式:

--innodb-autoinc-lock-mode=0 

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

innodb_autoinc_lock_mode=0

答案 3 :(得分:-2)

我知道这是一篇旧文章,但由于我也找不到合适的答案,我实际上找到了一种方法。您必须在if语句中包装查询。它通常插入查询或插入,并在重复的查询上搞乱有组织的自动增量顺序,以便常规插入使用:

$check_email_address = //select query here\\

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

而不是 INSERT AND ON DUPLICATE 在if语句中或之外使用 UPDATE SET WHERE QUERY 没关系, 替换入查询 似乎也有效