一位同事让我意识到一种非常奇怪的MySQL行为。
假设您有一个带有auto_increment字段的表和另一个设置为唯一的字段(例如用户名字段)。当尝试插入具有已存在于表中的用户名的行时,插入失败,如预期的那样。但是,在几次失败尝试后插入有效的新条目时,auto_increment值会增加。
例如,当我们的最后一个条目看起来像这样......
ID: 10
Username: myname
...我们在下一次插入时尝试五个具有相同用户名值的新条目,我们将创建一个新行,如下所示:
ID: 16
Username: mynewname
虽然这本身并不是一个大问题,但似乎是一个非常愚蠢的攻击向量,通过使用失败的插入请求充斥它来杀死一个表,正如MySQL参考手册所述:
“如果值大于可以存储在指定整数类型中的最大整数,则不会定义自动增量机制的行为。”
这是预期的行为吗?
答案 0 :(得分:31)
InnoDB
是一个交易引擎。
这意味着在以下场景中:
Session A
插入记录1
Session B
插入记录2
Session A
回滚,在session B
提交或回滚之前,可能存在间隙或session A
会锁定。
InnoDB
设计师(正如大多数其他交易引擎设计师一样)选择允许差距。
访问自动增量计数器时,
的表的并发性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 没关系, 替换入查询 似乎也有效