I campi di incremento automatico di MySQL si ripristinano da soli


12

Abbiamo una tabella MySQL che ha un campo auto-incrementante impostato come INT (11). Questa tabella contiene praticamente un elenco di lavori in esecuzione in un'applicazione. In qualsiasi momento durante il ciclo di vita dell'applicazione, la tabella potrebbe contenere migliaia di voci o essere completamente vuota (vale a dire che tutto è finito).

Il campo non ha una chiave esterna per nient'altro.

L'auto-incremento sembra reimpostarsi casualmente su zero, anche se non siamo mai stati in grado di intercettare il reset.

Il problema diventa evidente perché vediamo che il campo di auto-incremento arriva fino a, diciamo, a circa 600.000 record e poi un po 'più tardi il campo di auto-incremento sembra essere in esecuzione nei 1000 bassi.

È quasi come se l'incremento automatico si reimposti se la tabella è vuota.

È possibile e, se lo è, come lo spengo o cambio il modo in cui si reimposta?

In caso contrario, qualcuno ha una spiegazione del perché potrebbe farlo?

Grazie!


Quale versione di MySQL? Sono in uso transazioni o repliche?
magrezza

Risposte:


26

Il contatore dell'incremento automatico viene archiviato solo nella memoria principale, non sul disco.

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

Per questo motivo quando si riavvia il servizio (o server) si verificherà quanto segue:

Dopo l'avvio del server, per il primo inserimento in una tabella t, InnoDB esegue l'equivalente di questa istruzione: SELECT MAX (ai_col) FROM t FOR UPDATE;

InnoDB incrementa di uno il valore recuperato dall'istruzione e lo assegna alla colonna e al contatore di incremento automatico per la tabella. Se la tabella è vuota, InnoDB utilizza il valore 1.

Quindi, in parole povere, dopo l'avvio del servizio MySQL non ha idea di quale dovrebbe essere il valore di incremento automatico della tabella. Pertanto, quando si inserisce per la prima volta una riga, trova il valore massimo del campo che utilizza l'incremento automatico, aggiunge 1 a questo valore e utilizza il valore risultante. Se non ci sono righe, inizierà da 1.

Questo è stato un problema per noi, poiché utilizzavamo la tabella e la funzione di incremento automatico di mysql per gestire ordinatamente gli ID in un ambiente multi-thread in cui gli utenti venivano reindirizzati a un sito di pagamento di terze parti. Quindi abbiamo dovuto assicurarci che l'ID che la terza parte ci ha inviato e rispedito fosse univoco e che rimanesse tale (e ovviamente c'è la possibilità che l'utente annulli la transazione dopo essere stato reindirizzato).

Quindi stavamo creando una riga, ottenendo il valore di incremento automatico generato, eliminando la riga per mantenere pulita la tabella e inoltrando il valore al sito di pagamento. Quello che abbiamo finito per risolvere il problema del modo in cui InnoDB gestisce i valori di AI è stato il seguente:

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

Ciò mantiene sempre l'ultima transazioneId generata come riga nella tabella, senza far esplodere inutilmente la tabella.

Spero che possa aiutare chiunque altro a imbattersi in questo.

Modifica (18-04-2018) :

Come indicato di seguito da Finesse, il comportamento di questo è stato modificato in MySQL 8.0+.

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

La formulazione in quel registro di lavoro è nella migliore delle ipotesi difettosa, tuttavia sembra che InnoDB in quelle versioni più recenti ora supporti valori di autoinc persistenti nei riavvii.

-Gremio


Non male per un primo post, amico. +1.
Ceejayoz,

Dolce conoscenza lì Gremio. Grazie!! L'avevo completamente rimandato e archiviato il problema internamente come qualcosa da rivedere in un secondo momento, ma tornando su di esso la tua soluzione ha un fascino!
Hooligancat,

3

Abbiamo riscontrato questo problema e abbiamo scoperto che quando l'ottimizzazione della tabella veniva eseguita su una tabella vuota, veniva ripristinato anche il valore dell'incremento automatico. Vedi questo bug report MySQL .

Per ovviare al problema, puoi fare:

ALTER TABLE a AUTO_INCREMENT=3 ENGINE=innoDB;

Invece di OPTIMIZE TABLE.

Sembra che sia ciò che MySQL fa internamente (senza impostare il valore di Incremento automatico, ovviamente)


2

Solo uno scatto al buio: se l'applicazione sta usando a TRUNCATE TABLEper svuotare la tabella al termine dell'elaborazione, questo ripristinerà il campo dell'incremento automatico. Ecco una breve discussione sulla domanda. Sebbene quel link menzioni che InnoDB non ripristina auto_increments su un trunc, questo è stato segnalato come un bug e corretto alcuni anni fa.

Supponendo che la mia ipotesi sia corretta, potresti cambiare dal troncamento all'eliminazione per risolvere il problema.


Non pensavo che stessimo usando TRUNCATE, ma abbiamo dovuto controllare per essere sicuri. Sono arrivato persino al punto di verificare fino al livello del driver PHP per essere sicuro. Ma non lo eravamo. Apprezzo comunque la possibile soluzione.
Hooligancat,

1

Solo un reset esplicito di quel valore, o un drop / ricrea di quel campo, o altre simili operazioni violente dovrebbero mai resettare un contatore auto_increment. (Il TRUNCATE era davvero una buona teoria.) Sembra impossibile che tu stia improvvisamente avvolgendo un INT a 32 bit quando l'ultimo valore a cui sei testimone è solo 600k. Non dovrebbe assolutamente essere ripristinato solo perché la tabella si svuota. O hai un bug mysql o qualcosa nel tuo codice PHP. O il ragazzo nel cubicolo accanto ti sta giocando un brutto scherzo.

È possibile eseguire il debug attivando il registro binario , poiché conterrà dichiarazioni come questa in tutto:

SET INSERT_ID=3747670/*!*/;

Quindi almeno puoi vedere ogni dettaglio di ciò che sta accadendo a quella tabella, incluso proprio prima che il contatore si ripristini.


grazie per il suggerimento di debug. È passato un po 'di tempo da quando ho dovuto registrarmi in un Serverfault e apprezzo il tuo consiglio
Hooligancat

0

ALTER TABLE table_name ENGINE = MyISAM

Funziona per me. Il nostro tavolo è sempre molto piccolo, quindi non c'è bisogno di InnoDB.


Hai bisogno di transazioni?
Anthony Rutledge,

0

InnoDB non memorizza il valore di incremento automatico sul disco, quindi lo dimentica quando il server MySQL viene spento. Quando il MySQL viene avviato nuovamente, il motore InnoDB ripristina il valore di incremento automatico in questo modo: SELECT (MAX(id) + 1) AS auto_increment FROM table. Questo è un errore che viene risolto in MySQL versione 8.0.

Modificare il motore della tabella per risolvere il problema:

ALTER TABLE table ENGINE = MyISAM

Oppure aggiorna il server MySQL alla versione 8.0 quando viene rilasciato.

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.