2010-05-07 87 views
32

一位同事刚刚让我意识到一个非常奇怪的MySQL行为。为什么在失败的插入时MySQL自动增量会增加?

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

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

ID: 10 
Username: myname 

......我们试图用我们将创建一个新的行就像我们的下一个插入相同的用户名值五个新项目所以:

ID: 16 
Username: mynewname 

虽然这不是它本身似乎是一个非常愚蠢的攻击向量与失败的插入请求淹没它杀了表的一个大问题,因为MySQL参考手册的状态:

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

这是预期的行为吗?

+6

你的攻击向量似乎非的问题。如果你可以用失败的插入请求来洪水它,你不能同样用非失败的请求来洪水吗? – 2010-05-07 10:52:22

+0

您可以携带重现**代码**而不是手动解释吗? – 2010-05-07 10:55:59

+4

@martin smith:虽然这是真的,但我认为突然涌现的新用户会比auto_increment的沉默增加更明显,如果没有检查的话,它可能会很好地落在旁边。 – Sorcy 2010-05-07 12:40:40

回答

23

InnoDB是一个事务引擎。

这意味着,在以下情形:

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

,有任一间隙的可能性或session B将锁定,直到session A承诺或回滚。设计师(与大多数其他交易引擎设计师一样)选择允许差距。

documentation

当访问自动增长计数器,InnoDB采用了特殊的表级AUTO-INC锁,它保持对当前SQL语句的结束,而不是事务的结束。特殊的锁释放策略被引入以提高并发插入到含有AUTO_INCREMENT

表...只要

InnoDB使用内存自动增长计数器作为服务器运行。如上所述,当服务器停止并重新启动时,InnoDB将第一个INSERT的每个表的计数器重新初始化为该表。

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

+1

您的回答帮助我解决了很多自己的问题。谢谢。有没有办法绕过InnoDB有关自动增量的行为? – Aufwind 2011-08-08 22:23:46

+0

与Aufwind相同的问题。 – 2012-03-28 06:23:36

+1

@Ankit:请将它作为另一个问题发布,并在此处放置一个链接。 – Quassnoi 2012-03-28 09:06:06

4

不知道确切的内部,我会说是的,自动增量应该允许跳过的值做失败插入。比方说,你正在做一个银行交易,或者其他整个交易和多笔记录都是一笔一笔或一笔交易。如果您尝试插入,获取ID,然后使用该交易ID标记所有后续详细信息并插入详细记录,则需要确保您的合格唯一性。如果你有多人关闭数据库,他们也需要确保他们获得自己的事务ID,以便在他们的事务被提交时不与你的冲突。如果第一笔交易失败了,没有任何伤害,并且没有下游的悬挂成分。

+0

其使用skkiping性质的自动增量,但我想知道的是,这是任何方式来阻止这个skkiping – 2012-03-28 09:08:43

+2

@Ankit,不,你不能停止跳过。跟踪分配的最后一个ID的文件头始终增加。如果出现问题,并且有10人正在输入交易并且3人中止,那么您永远不会希望仅仅使用ID来回填此类中止的交易。 – DRapp 2012-03-28 10:11:33

+0

thanx结算dout – 2012-03-28 10:39:08

-1

我知道这是一篇旧文章,但由于我也找不到正确的答案,我实际上找到了一种方法来做到这一点。你必须在if语句中包装你的查询。它通常插入查询或插入和重复querys是搞乱了有组织的自动递增顺序,以便定期插入使用:

和替代insert和DUPLICATE使用UPDATE SET WHERE QUERY或外部的if语句并不重要,一个REPLACE INTO查询也似乎工作

+2

我downvoting这个答案,因为它根本不适用。是的,您可以在尝试插入之前检查记录是否存在,但这完全在旁边。 – Mave 2016-03-23 09:53:08