Impedisci il ripristino dell'ID auto_increment nel database Innodb dopo il riavvio del server


11

Di recente ho letto che, a causa del modo in cui InnoDB ricalcola il valore AUTO_INCREMENT al riavvio del server, è possibile riutilizzare i propri ID in qualsiasi record nella parte alta dell'elenco ID.

Normalmente, questo non è un problema, perché quando un utente viene eliminato tutto ciò che è associato all'ID viene eliminato anche da altre tabelle.

Ma sto deliberatamente lasciando orfani i loro post sul forum, etichettati come "Inserito da = Utente # 123 =", in modo che le conversazioni passate vengano mantenute. Chiaramente, se un ID viene riutilizzato, questo sarà un problema.

Non ho mai avuto questo problema prima perché c'erano sempre abbastanza nuovi utenti da rendere improbabile che un ID venisse riutilizzato in questo modo. Tuttavia, nel mio nuovo progetto le iscrizioni sono rare e le cancellazioni degli utenti inattive sono frequenti (soprattutto perché gli account "Open Alpha" durano solo tre giorni come anteprima) e tale riutilizzo degli ID è avvenuto tre per tre ora.

Ho "risolto" il problema salvando il valore corretto per AUTO_INCREMENT altrove e usando quello invece di fare affidamento sul valore interno. Esiste un modo effettivo per far ricordare a InnoDB l'ultimo valore effettivo?


Hai l'articolo che leggi?
gbn,

@gbn Il link per l'articolo dev.mysql.com/doc/refman/5.1/en/…
Naveen Kumar

Per il riferimento questo è bugs.mysql.com/bug.php?id=199
Laurynas Biveinis

ALTER TABLE table_name ENGINE = MyISAM Funziona per me. Il nostro tavolo è sempre molto piccolo, quindi non c'è bisogno di InnoDB.

1
@QuickFix Dovresti aggiungere alcuni dettagli sul perché funzioni .
Max Vernon,

Risposte:


5

(evitando il problema non eliminando mai)

Dal momento che si desidera conservare le "Posted by =User #123="informazioni dopo aver eliminato l'utente con id=123, è possibile anche considerare l'utilizzo di 2 tabelle per la memorizzazione dei dati degli utenti. Uno per gli Activeutenti e uno per tutti (compresi quelli eliminati dagli utenti attivi). E non eliminare mai quegli ID dalla AllUsertabella:

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

Questo ovviamente complicherà l'operazione di inserimento di un nuovo utente. Ogni nuovo utente significherà 2 inserti, uno in ogni tabella. Tuttavia, l'eliminazione di un utente avverrà solo eliminando dalla ActiveUsertabella. Tutti gli FK verranno eliminati a cascata, ad eccezione dei post del forum, che faranno riferimento alla Allusertabella (dove non verrà mai eseguita l' eliminazione).


4

Non esiste un modo naturale per farlo se non per utilizzare information_schema.tables per registrare tutte le colonne con l'opzione auto_increment.

È possibile raccogliere quelle colonne come segue:

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;

Crea uno script che ripristinerà i valori di 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}

Potresti quindi fare una delle due cose:

OPZIONE n. 1: eseguire lo script manualmente dopo l'avvio

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

OPZIONE n. 2: fare eseguire lo script mysqld prima di consentire le connessioni

Dovresti aggiungere questa opzione

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

In questo modo, ogni volta che riavvii mysql, questo script viene eseguito all'inizio. Dovrai ricordarti di rigenerare /var/lib/mysql/ResetAutoInc.sql prima di fare un riavvio mysql pianificato.


3

I documenti 5.5 suggeriscono di archiviare il valore di autoincremento altrove come hai già fatto.

Una soluzione alternativa sarebbe quella di emulare una SEQUENZA in modo da non utilizzare l'auto-incremento nella tabella stessa. Questo è stato discusso su SO prima e ancora . Il blog sulle prestazioni di MySQL lo menziona.

Ancora un altro dato di MySQL che scopa che altri RDBMS non hanno ...


2

Basta non eliminare l'utente. L'integrità relazionale è più importante. Se devi farlo per motivi di privacy o altro, cambia semplicemente il nome utente in "cancellato" e cancella tutti gli altri campi.


1

Questa è una vecchia domanda e ancora rilevante.

1) Questo comportamento è stato risolto in Mysql 8.0.

2) Una soluzione è utilizzare una riga fittizia per i tuoi dati, per mantenere AUTO_INCREMENT al di sopra di un determinato valore. Non molto comodo a seconda di ciò che si sta memorizzando, ma in alcuni casi è una soluzione semplice.


0

Avevamo bisogno di questo estrapolato una soluzione per il nostro sistema basato sulle istruzioni di questo post. Se questo può aiutare chiunque a raggiungere il proprio obiettivo in un modo ancora più semplice.

Il nostro sistema utilizza un modello di tabella tombstone per memorizzare gli elementi eliminati perché eseguiamo la sincronizzazione bidirezionale su sistemi disconnessi, quindi utilizziamo questo codice per abbinare le tabelle tombstone con le loro tabelle live ed estrarre il valore più alto possibile :)

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 ;
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.