Das automatische Inkrementieren von MySQL-Feldern wird von selbst zurückgesetzt

12

Wir haben eine MySQL-Tabelle mit einem automatisch inkrementierenden Feld, das als INT (11) festgelegt ist. In dieser Tabelle wird eine Liste der Jobs gespeichert, die in einer Anwendung ausgeführt werden. Zu jedem Zeitpunkt während der Lebensdauer der Anwendung kann die Tabelle Tausende von Einträgen enthalten oder vollständig leer sein (dh alles ist beendet).

Das Feld ist für nichts anderes fremdverschlüsselt.

Das Auto-Inkrement scheint sich zufällig auf Null zurückzusetzen, obwohl es uns noch nie gelungen ist, das Zurücksetzen einzufangen.

Das Problem wird offensichtlich, weil das Auto-Inkrement-Feld auf etwa 600.000 Datensätze ansteigt und eine Weile später das Auto-Inkrement-Feld in den niedrigen 1000er-Werten zu laufen scheint.

Es ist fast so, als würde sich das Auto-Inkrement von selbst zurücksetzen, wenn die Tabelle leer ist.

Ist dies möglich und wenn ja, wie schalte ich es aus oder ändere die Art und Weise, in der es zurückgesetzt wird?

Wenn dies nicht der Fall ist, hat jemand eine Erklärung, warum dies möglicherweise der Fall ist?

Vielen Dank!

Hooligancat
quelle
Welche Version von MySQL? Werden Transaktionen oder Replikationen verwendet?
Thinice

Antworten:

26

Der Auto-Inkrement-Zähler wird nur im Hauptspeicher und nicht auf der Festplatte gespeichert.

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

Aus diesem Grund geschieht beim Neustart des Dienstes (oder Servers) Folgendes:

Nach einem Serverstart führt InnoDB beim ersten Einfügen in eine Tabelle t das Äquivalent dieser Anweisung aus: SELECT MAX (ai_col) FROM t FOR UPDATE;

InnoDB erhöht den von der Anweisung abgerufenen Wert um eins und weist ihn der Spalte und dem Auto-Increment-Zähler für die Tabelle zu. Wenn die Tabelle leer ist, verwendet InnoDB den Wert 1.

Nachdem der MySQL-Dienst gestartet wurde, hat er im Klartext keine Ahnung, wie hoch der Auto-Inkrement-Wert für Ihre Tabelle sein sollte. Wenn Sie also zum ersten Mal eine Zeile einfügen, wird der Maximalwert des Felds ermittelt, das die automatische Inkrementierung verwendet, 1 zu diesem Wert hinzugefügt und der resultierende Wert verwendet. Wenn keine Zeilen vorhanden sind, wird mit 1 begonnen.

Dies war ein Problem für uns, da wir die Tabelle und die automatische Inkrementierungsfunktion von mysql verwendeten, um IDs in einer Umgebung mit mehreren Threads sauber zu verwalten, in der Benutzer zu einer Zahlungswebsite eines Drittanbieters weitergeleitet wurden. Daher mussten wir sicherstellen, dass die ID, die der Dritte erhalten und an uns zurückgesandt hat, eindeutig ist und dies auch bleibt (und natürlich besteht die Möglichkeit, dass der Benutzer die Transaktion nach der Weiterleitung storniert).

Also haben wir eine Zeile erstellt, den generierten Wert für die automatische Inkrementierung abgerufen, die Zeile gelöscht, um die Tabelle sauber zu halten, und den Wert an die Zahlungssite weitergeleitet. Am Ende haben wir Folgendes getan, um das Problem zu beheben, wie InnoDB AI-Werte verarbeitet:

$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); 

Auf diese Weise wird immer die letzte als Zeile in der Tabelle generierte Transaktions-ID beibehalten, ohne dass die Tabelle unnötig in die Luft gesprengt wird.

Hoffe, das hilft allen anderen, die darauf stoßen könnten.

Bearbeiten (2018-04-18) :

Wie Finesse weiter unten erwähnte, wurde das Verhalten in MySQL 8.0+ geändert.

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

Der Wortlaut in diesem Worklog ist bestenfalls fehlerhaft, es scheint jedoch, dass InnoDB in diesen neueren Versionen nun persistente Autoinc-Werte über Neustarts hinweg unterstützt.

-Gremio

Gremio
quelle
Nicht schlecht für einen ersten Beitrag, Alter. +1.
ceejayoz
Süßes Stück Wissen gibt es Gremio. Vielen Dank!! Ich hatte dies komplett aufgeschoben und das Problem intern archiviert, um es zu einem späteren Zeitpunkt noch einmal zu überprüfen, aber Ihre Lösung hat sich bewährt!
Hooligancat
3

Wir haben dieses Problem festgestellt und festgestellt, dass der Wert für die automatische Inkrementierung ebenfalls zurückgesetzt wurde, als die Optimierungstabelle für eine leere Tabelle ausgeführt wurde. Siehe diesen MySQL-Fehlerbericht .

Als Problemumgehung können Sie Folgendes tun:

ALTER TABLE a AUTO_INCREMENT=3 ENGINE=innoDB;

Anstelle von OPTIMIZE TABLE.

Es sieht so aus, als ob MySQL dies intern tut (natürlich ohne den Wert für das automatische Inkrementieren festzulegen).

Stange
quelle
2

Nur ein Schuss in die Dunkelheit - wenn die Anwendung a verwendet TRUNCATE TABLE, um die Tabelle nach Abschluss der Verarbeitung zu leeren, wird das Auto-Inkrement-Feld zurückgesetzt. Hier ist eine kurze Diskussion zu dieser Frage. Dieser Link erwähnt zwar, dass InnoDB auto_increments auf einem Trunc nicht zurücksetzt, dies wurde jedoch als Fehler gemeldet und vor einigen Jahren behoben.

Unter der Annahme, dass meine Vermutung richtig ist, können Sie vom Abschneiden zum Löschen wechseln, um das Problem zu beheben.

dsolimano
quelle
Ich dachte nicht, dass wir TRUNCATE verwenden, aber wir mussten es überprüfen, um sicherzugehen. Es ging sogar so weit, sich bis auf die PHP-Treiberebene zu verifizieren, um dies sicherzustellen. Aber das waren wir nicht. Schätzen Sie die mögliche Lösung.
Hooligancat
1

Nur ein explizites Zurücksetzen dieses Wertes oder ein Löschen / erneutes Erstellen dieses Feldes oder eine ähnliche gewaltsame Operation sollte einen auto_increment-Zähler jemals zurücksetzen. (Die TRUNCATE war eine wirklich gute Theorie.) Es scheint unmöglich, dass Sie plötzlich ein 32-Bit-INT einbinden, wenn der letzte Wert, den Sie sehen, nur 600 KB beträgt. Es sollte definitiv nicht zurückgesetzt werden, nur weil der Tisch leer ist. Sie haben entweder einen Mysql-Fehler oder etwas in Ihrem PHP-Code. Oder der Typ in der Kabine nebenan spielt dir einen Streich.

Sie können das Debuggen durchführen, indem Sie das Binärprotokoll aktivieren , da es Anweisungen wie die folgende enthält:

SET INSERT_ID=3747670/*!*/;

Dann können Sie zumindest jedes Detail dessen sehen, was mit dieser Tabelle passiert, auch bevor der Zähler zurückgesetzt wird.

IcarusNM
quelle
Danke für den Debug-Tipp. Es ist schon eine Weile her, dass ich einen Serverfehler einchecken musste und ich weiß deinen Tipp zu schätzen
Hooligancat
0

ALTER TABLE table_name ENGINE = MyISAM

Arbeitet für mich. Unser Tisch wird immer sehr klein gehalten, sodass InnoDB nicht benötigt wird.

Schnelle Lösung
quelle
Benötigen Sie Transaktionen?
Anthony Rutledge
0

InnoDB speichert keinen Auto-Inkrement-Wert auf der Festplatte und vergisst ihn daher, wenn der MySQL-Server heruntergefahren wird. Wenn der MySQL wieder gestartet wird, stellt die InnoDB - Engine diese Weise wird die Autoinkrement - Wert: SELECT (MAX(id) + 1) AS auto_increment FROM table. Dies ist ein Fehler , der in MySQL Version 8.0 behoben ist .

Ändern Sie die Tabellen-Engine, um das Problem zu beheben:

ALTER TABLE table ENGINE = MyISAM

Oder aktualisieren Sie den MySQL-Server bei Veröffentlichung auf Version 8.0.

Finesse
quelle