MySQL自动增量字段自行重置


12

我们有一个MySQL表,该表具有一个设置为INT(11)的自动递增字段。该表几乎存储了应用程序中正在运行的作业列表。在应用程序生命周期中的任何给定时刻,该表很可能包含数千个条目或完全为空(即,一切都已完成)。

该字段没有其他任何外键。

尽管我们从未真正能够捕获到重置,但自动增量似乎将自身随机重置为零。

这个问题变得很明显,因为我们看到自动增量字段最多可以存储600,000条记录,然后过一会儿,自动增量字段似乎在低1000的范围内运行。

如果表为空,则好像自动递增会自行重置。

是否可能,如果可以,如何关闭它或更改其重置方式?

如果不是,是否有人解释为什么会这样做?

谢谢!


什么版本的MySQL?是否正在使用事务或复制?
Thinice 2011年

Answers:


26

自动增量计数器仅存储在主存储器中,而不存储在磁盘上。

http://dev.mysql.com/doc/refman/4.1/en/innodb-auto-increment-handling.html

因此,当服务(或服务器)重新启动时,将发生以下情况:

服务器启动后,对于第一次插入到表t中,InnoDB执行以下语句的等效项:SELECT MAX(ai_col)FROM t FOR UPDATE;

InnoDB将语句检索的值加1,然后将其分配给列和表的自动递增计数器。如果表为空,则InnoDB使用值1。

因此,以通俗的英语来说,在MySQL服务启动之后,它不知道表的自动增量值应该是多少。因此,当您第一次插入一行时,它会找到使用自动增量的字段的最大值,在该值上加1,然后使用结果值。如果没有行,它将从1开始。

这对我们来说是个问题,因为我们正在使用表和mysql的自动增量功能在多线程环境中整齐地管理ID,在该环境中,用户被重定向到第三方付款站点。因此,我们必须确保第三方获取并发送回给我们的ID是唯一的,并且将保持这种方式(当然,用户重定向后,用户有可能取消交易)。

因此,我们要创建一行,获取生成的自动增量值,删除该行以保持表整洁,然后将该值转发到支付站点。为了解决InnoDB处理AI值的方式问题,我们最终做了以下工作:

$query = "INSERT INTO transactions_counter () VALUES ();";
mysql_query($query);
$transactionId = mysql_insert_id();
$previousId = $transactionId - 1;
$query = "DELETE FROM transactions_counter WHERE transactionId='$previousId';";
mysql_query($query); 

这始终将最新生成的transactionId保留为表中的一行,而不会不必要地破坏该表。

希望对其他可能遇到此问题的人有所帮助。

编辑(2018-04-18)

正如下面的Finesse所述,它的行为似乎已在MySQL 8.0+中进行了修改。

https://dev.mysql.com/worklog/task/?id=6204

该工作日志中的措词充其量是错误的,但是,在那些较新版本中的InnoDB现在似乎在重新启动后支持持久的autoinc值。

-格雷米奥


老兄,对于第一篇文章来说还不错。+1。
ceejayoz 2012年

格雷米奥(Gremio)有一点甜蜜的知识。谢谢!!我已经完全推迟了此事,并在内部将问题存档,以备日后查阅,但是重新解决它,您的解决方案就很有吸引力!
Hooligancat

3

我们已经遇到了这个问题,并且发现当优化表在空表上运行时,自动增量值也会被重置。请参阅此MySQL错误报告

作为一种解决方法,您可以执行以下操作:

ALTER TABLE a AUTO_INCREMENT=3 ENGINE=innoDB;

代替OPTIMIZE TABLE

看来这是MySQL内部执行的操作(显然,未设置自动增量值)


2

只是黑暗中的一枪-如果应用程序TRUNCATE TABLE在处理完成后使用a 清空表,这将重置auto-increment字段。这里是关于这个问题的简短讨论。尽管该链接提到InnoDB不会在trunc上重置auto_increments,但该错误已报告并于几年前得到修复。

假设我的猜测是正确的,则可以从截断更改为删除以解决问题。


我不认为我们正在使用TRUNCATE,但是我们必须进行检查以确保。甚至可以验证到PHP驱动程序级别以确保。但是我们不是。不过赞赏可能的解决方案。
Hooligancat

1

只有对该值的显式重置,对该字段的删除/重新创建或其他类似的暴力操作,才应该重置auto_increment计数器。(TRUNCATE是一个非常好的理论。)当您目睹的最后一个值仅为600k时,似乎不可能突然包装32位INT。它绝对不应该仅仅因为表为空而重置。您可能有mysql错误或PHP代码中的某些内容。或是隔壁小隔间里的那个家伙在耍你。

您可以通过打开二进制日志进行调试,因为在整个过程中它将包含如下语句:

SET INSERT_ID=3747670/*!*/;

然后,至少您可以看到该表的所有详细信息,包括在计数器重置之前。


感谢您的调试提示。自从我需要签入Serverfault以来已经有一段时间了,感谢您的提示
Hooligancat


0

InnoDB不会在磁盘上存储自动增量值,因此在MySQL服务器关闭时会忘记自动增量值。当再次启动MySQL时,InnoDB引擎将通过以下方式恢复自动增量值:SELECT (MAX(id) + 1) AS auto_increment FROM table。这是一个错误固定在MySQL 8.0版。

更改表引擎以解决问题:

ALTER TABLE table ENGINE = MyISAM

或在发布MySQL服务器时将其更新到8.0版。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.