Empêcher la réinitialisation de l'ID auto_increment dans la base de données Innodb après le redémarrage du serveur

11

J'ai récemment lu qu'en raison de la façon dont InnoDB recalcule la valeur AUTO_INCREMENT lorsque le serveur redémarre, tous les enregistrements situés dans la partie supérieure de la liste d'ID peuvent voir leurs ID réutilisés.

Normalement, ce n'est pas un problème, car lorsqu'un utilisateur est supprimé, tout ce qui est associé à l'ID est également supprimé des autres tables.

Mais je laisse délibérément leurs messages de forum orphelins, étiquetés "Posté par = Utilisateur # 123 =", afin que les conversations passées soient conservées. De toute évidence, si un ID est réutilisé, ce sera un problème.

Je n'ai jamais eu ce problème auparavant car il y avait toujours suffisamment de nouveaux utilisateurs pour qu'il soit peu probable qu'un ID soit réutilisé de cette manière. Cependant, sur mon nouveau projet, les inscriptions sont rares et les suppressions d'utilisateurs inactifs sont fréquentes (surtout depuis que les comptes "Open Alpha" ne durent que trois jours en tant qu'aperçu), et une telle réutilisation des ID s'est produite trois à trois maintenant.

J'ai "résolu" le problème en enregistrant la valeur correcte pour AUTO_INCREMENT ailleurs et en l'utilisant au lieu de compter sur la valeur interne. Existe-t-il un moyen réel pour qu'InnoDB se souvienne de la dernière valeur réelle?

Naveen Kumar
la source
Avez-vous l'article que vous avez lu?
gbn
@gbn Le lien vers l'article dev.mysql.com/doc/refman/5.1/en/…
Naveen Kumar
Pour la référence c'est bugs.mysql.com/bug.php?id=199
Laurynas Biveinis
ALTER TABLE nom_table ENGINE = MyISAM Fonctionne pour moi. Notre table est toujours très petite, donc pas besoin d'InnoDB.
1
@QuickFix Vous devez ajouter quelques détails sur la raison pour laquelle cela fonctionne.
Max Vernon

Réponses:

5

(éviter le problème en ne supprimant jamais)

Étant donné que vous souhaitez conserver les "Posted by =User #123="informations après avoir supprimé l'utilisateur avec id=123, vous pouvez également envisager d'utiliser 2 tableaux pour stocker les données des utilisateurs. Un pour les Activeutilisateurs et un pour tous (y compris ceux supprimés des utilisateurs actifs). Et ne supprimez jamais ces identifiants du AllUsertableau:

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

Cela compliquera bien sûr l'opération d'insertion d'un nouvel utilisateur. Tout nouvel utilisateur signifiera 2 insertions, une dans chaque tableau. Cependant, la suppression d'un utilisateur se fera ActiveUseruniquement par suppression du tableau. Tous les FK seront supprimés en cascade, à l'exception des messages du forum, qui feront référence à la Allusertable (où aucune suppression ne se produira jamais).

ypercubeᵀᴹ
la source
4

Il n'y a aucun moyen naturel de le faire, sauf d'utiliser information_schema.tables pour enregistrer toutes les colonnes avec l'option auto_increment.

Vous pouvez collecter ces colonnes comme suit:

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;

Créez un script qui réinitialisera les valeurs auto_increment

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}

Vous pouvez alors faire l'une des deux choses suivantes:

OPTION # 1: exécuter le script manuellement après le démarrage

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

OPTION # 2: Demandez à mysqld d'exécuter le script avant d'autoriser les connexions

Vous devez ajouter cette option

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

De cette façon, chaque fois que vous redémarrez mysql, ce script est exécuté au début. Vous devrez vous rappeler de régénérer /var/lib/mysql/ResetAutoInc.sql avant de faire un redémarrage prévu de mysql.

RolandoMySQLDBA
la source
3

La documentation 5.5 suggère de stocker la valeur d'incrémentation automatique ailleurs comme vous l'avez déjà fait.

Une solution alternative serait d'émuler une SEQUENCE afin de ne pas utiliser l'incrémentation automatique dans la table elle-même. Cela a été discuté sur le SO avant et à nouveau . Le blog MySQL Performance le mentionne.

Pourtant, une autre donnée MySQL vissée que les autres SGBDR n'ont pas ...

gbn
la source
2

Ne supprimez simplement pas l'utilisateur. L'intégrité relationnelle est plus importante. Si vous devez le faire pour des raisons de confidentialité ou autre, changez simplement le nom d'utilisateur en «supprimé» et effacez tous les autres champs.

Jannes
la source
1

C'est une vieille question et toujours d'actualité.

1) Ce problème est en cours de correction dans Mysql 8.0.

2) Une solution consiste à utiliser une ligne fictive pour vos données, afin de maintenir l'AUTO_INCREMENT au-dessus d'une certaine valeur. Pas super pratique selon ce que vous stockez, mais c'est une solution simple dans certains cas.

Garr Godfrey
la source
0

Nous avions besoin d'une solution extrapolée pour notre propre système basée sur les instructions de ce post. Si cela peut aider n'importe qui à atteindre son objectif de manière encore plus simple.

Notre système utilise un modèle de table tombstone pour stocker les éléments supprimés car nous effectuons une synchronisation bidirectionnelle sur les systèmes déconnectés, nous utilisons donc ce code pour faire correspondre les tables tombstone avec leurs tables en direct et extraire la valeur la plus élevée possible :)

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
la source