Verhindern Sie das Zurücksetzen der auto_increment-ID in der Innodb-Datenbank nach dem Neustart des Servers

11

Ich habe kürzlich gelesen, dass aufgrund der Art und Weise, wie InnoDB den AUTO_INCREMENT-Wert beim Neustart des Servers neu berechnet, die IDs aller Datensätze am oberen Ende der ID-Liste möglicherweise wiederverwendet werden.

Normalerweise ist dies kein Problem, da beim Löschen eines Benutzers alles, was mit der ID verknüpft ist, auch aus anderen Tabellen gelöscht wird.

Aber ich lasse ihre Forenbeiträge absichtlich verwaist und als "Gepostet von = Benutzer # 123 =" gekennzeichnet, damit frühere Konversationen beibehalten werden. Sollte eine ID wiederverwendet werden, ist dies natürlich ein Problem.

Ich hatte dieses Problem noch nie zuvor, da es immer genug neue Benutzer gab, um es unwahrscheinlich zu machen, dass eine ID auf diese Weise wiederverwendet wird. Bei meinem neuen Projekt sind Anmeldungen jedoch selten und inaktive Benutzerlöschungen häufig (zumal die "Open Alpha" -Konten nur drei Tage als Vorschau gültig sind), und eine solche ID-Wiederverwendung wurde bereits drei für drei Mal durchgeführt.

Ich habe das Problem "behoben", indem ich den korrekten Wert für AUTO_INCREMENT an anderer Stelle gespeichert und diesen verwendet habe, anstatt mich auf den internen Wert zu verlassen. Gibt es eine Möglichkeit, InnoDB den tatsächlichen letzten Wert merken zu lassen?

Naveen Kumar
quelle
Haben Sie den Artikel, den Sie gelesen haben?
Gbn
@gbn Der Link für den Artikel dev.mysql.com/doc/refman/5.1/en/…
Naveen Kumar
Als Referenz ist dies bugs.mysql.com/bug.php?id=199
Laurynas Biveinis
ALTER TABLE Tabellenname ENGINE = MyISAM Funktioniert für mich. Unser Tisch ist immer sehr klein gehalten, daher ist InnoDB nicht erforderlich.
1
@QuickFix Sie sollten einige Details hinzufügen, warum dies funktioniert.
Max Vernon

Antworten:

5

(Vermeiden Sie das Problem, indem Sie es niemals löschen.)

Da Sie die "Posted by =User #123="Informationen behalten möchten, nachdem Sie den Benutzer mit gelöscht haben id=123, können Sie auch zwei Tabellen zum Speichern von Benutzerdaten verwenden. Eine für ActiveBenutzer und eine für alle (einschließlich der von den aktiven Benutzern gelöschten). Und löschen Sie niemals diese IDs aus der AllUserTabelle:

CREATE TABLE AllUser
( user_id INT AUTO_INCREMENT
, ...
, PRIMARY KEY (user_id)
) ;

------
--- Forum posts FK should reference the `AllUser` table

CREATE TABLE ActiveUser
( user_id INT 
, ...
, PRIMARY KEY (user_id)
, FOREIGN KEY (user_id)
    REFERENCES AllUser (user_id)
) ;

------
--- All other FKs should reference the `ActiveUser` table

Dies erschwert natürlich das Einfügen eines neuen Benutzers. Jeder neue Benutzer bedeutet 2 Einfügungen, eine in jeder Tabelle. Das Löschen eines Benutzers erfolgt jedoch nur durch Löschen aus der ActiveUserTabelle. Alle FKs werden mit Kaskadierung gelöscht, mit Ausnahme der Forenbeiträge, die auf die AlluserTabelle verweisen (wo niemals gelöscht wird).

ypercubeᵀᴹ
quelle
4

Es gibt keine natürliche Möglichkeit, dies zu tun, außer mit information_schema.tables alle Spalten mit der Option auto_increment aufzuzeichnen.

Sie können diese Spalten wie folgt sammeln:

CREATE TABLE mysql.my_autoinc ENGINE=MyISAM
SELECT table_schema,table_name,auto_increment
FROM information_schema.tables WHERE 1=2;
ALTER TABLE mysql.my_autoinc ADD PRIMARY KEY (table_schema,table_name);
INSERT INTO mysql.my_autoinc
SELECT table_schema,table_name,auto_increment
FROM information_schema.tables WHERE auto_increment IS NOT NULL;

Erstellen Sie ein Skript, das die Werte für auto_increment zurücksetzt

AUTOINC_SCRIPT=/var/lib/mysql/ResetAutoInc.sql
mysql -u... -p... -AN -e"SELECT CONCAT('ALTER TABLE ',table_schema,'.',table_name,' AUTO_INCREMENT=',auto_increment,';') FROM mysql.my_autoinc" > ${AUTOINC_SCRIPT}

Sie können dann eines von zwei Dingen tun:

OPTION 1: Führen Sie das Skript nach dem Start manuell aus

mysql> source /var/lib/mysql/ResetAutoInc.sql

OPTION 2: Lassen Sie mysqld ein Skript ausführen, bevor Sie Verbindungen zulassen

Sie müssten diese Option hinzufügen

[mysqld]
init-file=/var/lib/mysql/ResetAutoInc.sql

Auf diese Weise wird dieses Skript jedes Mal, wenn Sie mysql neu starten, am Anfang ausgeführt. Sie müssen daran denken, /var/lib/mysql/ResetAutoInc.sql neu zu generieren, bevor Sie einen geplanten MySQL-Neustart durchführen.

RolandoMySQLDBA
quelle
3

In den 5.5-Dokumenten wird vorgeschlagen , den Wert für die automatische Inkrementierung an einer anderen Stelle zu speichern, wie Sie es bereits getan haben.

Eine alternative Lösung wäre, eine SEQUENZ zu emulieren, damit Sie in der eigentlichen Tabelle selbst kein automatisches Inkrement verwenden. Dies wurde SO diskutiert, bevor und wieder . Der MySQL Performance Blog erwähnt es.

Noch eine MySQL-Datenverschraubung, die andere RDBMS nicht haben ...

gbn
quelle
2

Löschen Sie den Benutzer einfach nicht. Relationale Integrität ist wichtiger. Wenn Sie dies aus Datenschutzgründen oder aus anderen Gründen tun müssen, ändern Sie einfach den Benutzernamen in "gelöscht" und löschen Sie alle anderen Felder.

Jannes
quelle
1

Dies ist eine alte Frage und immer noch relevant.

1) Dieses Verhalten wird in MySQL 8.0 behoben.

2) Eine Lösung besteht darin, eine Dummy-Zeile für Ihre Daten zu verwenden, um AUTO_INCREMENT über einem bestimmten Wert zu halten. Je nachdem, was Sie speichern, nicht besonders praktisch, ist aber in einigen Fällen eine einfache Lösung.

Garr Godfrey
quelle
0

Wir hatten dafür eine extrapolierte Lösung für unser eigenes System basierend auf den Anweisungen in diesem Beitrag. Wenn dies jemandem helfen kann, sein Ziel noch einfacher zu erreichen.

Unser System verwendet ein Tombstone-Tabellenmuster, um gelöschte Elemente zu speichern, da wir auf getrennten Systemen eine bidirektionale Synchronisierung durchführen. Daher verwenden wir diesen Code, um die Tombstone-Tabellen mit ihren Live-Tabellen abzugleichen und den höchstmöglichen Wert zu extrahieren :)

DROP PROCEDURE IF EXISTS `reset_auto_increments`;
DELIMITER $
CREATE PROCEDURE reset_auto_increments()
BEGIN

    DECLARE done INT DEFAULT 0;
    DECLARE schemaName VARCHAR(255) DEFAULT '';
    DECLARE liveTableName VARCHAR(255) DEFAULT '';
    DECLARE tombstoneTableName VARCHAR(255) DEFAULT '';
    DECLARE liveAutoIncrement INT DEFAULT 0;
    DECLARE tombstoneAutoIncrement INT DEFAULT 0;
    DECLARE newAutoIncrement INT DEFAULT 0;

    DECLARE autoIncrementPairs CURSOR FOR 
        SELECT
            liveTables.TABLE_SCHEMA AS schemaName,
            liveTables.TABLE_NAME AS liveTable, 
            tombstoneTables.TABLE_NAME AS tombstoneTable,
            liveTables.AUTO_INCREMENT AS live_auto_increment,
            tombstoneTables.AUTO_INCREMENT AS tombstone_auto_increment,
            GREATEST(liveTables.AUTO_INCREMENT, tombstoneTables.AUTO_INCREMENT) AS new_auto_increment
        FROM 
            information_schema.tables AS liveTables
            JOIN information_schema.tables AS tombstoneTables
                ON liveTables.TABLE_SCHEMA = tombstoneTables.TABLE_SCHEMA
                    AND CONCAT('deleted', UCASE(LEFT(liveTables.TABLE_NAME, 1)), SUBSTRING(liveTables.TABLE_NAME, 2))
                        = tombstoneTables.TABLE_NAME
        WHERE
            GREATEST(liveTables.AUTO_INCREMENT, tombstoneTables.AUTO_INCREMENT) IS NOT NULL;

    DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;

    SET done = 0;

    SET schemaName = '';
    SET liveTableName = '';
    SET tombstoneTableName = '';
    SET liveAutoIncrement = 0;
    SET tombstoneAutoIncrement = 0;
    SET newAutoIncrement = 0;

    OPEN autoIncrementPairs;
    REPEAT

        FETCH autoIncrementPairs INTO 
            schemaName, 
            liveTableName, 
            tombstoneTableName, 
            liveAutoIncrement, 
            tombstoneAutoIncrement, 
            newAutoIncrement;

        SET @statement = CONCAT('ALTER TABLE ', schemaName, '.', liveTableName, ' AUTO_INCREMENT=', newAutoIncrement);
        PREPARE updateAutoIncrementStatement FROM @statement;
        EXECUTE updateAutoIncrementStatement;
        DEALLOCATE PREPARE updateAutoIncrementStatement;

    UNTIL done END REPEAT;

    CLOSE autoIncrementPairs;

END$

DELIMITER ;
Mathieu Dumoulin
quelle