Les champs d'incrémentation automatique de MySQL sont réinitialisés par eux-mêmes


12

Nous avons une table MySQL qui a un champ auto-incrémenté défini comme INT (11). Ce tableau stocke à peu près une liste des travaux en cours d'exécution dans une application. À tout moment pendant la durée de vie de l'application, le tableau peut contenir des milliers d'entrées ou être complètement vide (c'est-à-dire que tout est terminé).

Le champ n'est associé à aucune clé étrangère.

L'incrémentation automatique semble se remettre à zéro de façon aléatoire, bien que nous n'ayons jamais pu piéger la réinitialisation.

Le problème devient évident parce que nous voyons le champ d'incrémentation automatique atteindre, disons, 600 000 enregistrements environ, puis un peu plus tard, le champ d'incrémentation automatique semble fonctionner dans les 1000 bas.

C'est presque comme si l'incrémentation automatique se réinitialise si la table est vide.

Est-ce possible et si c'est le cas, comment puis-je le désactiver ou changer la manière dont il se réinitialise?

Si ce n'est pas le cas, quelqu'un a-t-il une explication de la raison pour laquelle il pourrait faire cela?

Merci!


Quelle version de MySQL? Les transactions ou la réplication sont-elles utilisées?
Thinice

Réponses:


26

Le compteur d'incrémentation automatique est stocké uniquement dans la mémoire principale, pas sur le disque.

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

Pour cette raison, lorsque le service (ou le serveur) redémarre, les événements suivants se produisent:

Après un démarrage de serveur, pour la première insertion dans une table t, InnoDB exécute l'équivalent de cette instruction: SELECT MAX (ai_col) FROM t FOR UPDATE;

InnoDB incrémente de un la valeur récupérée par l'instruction et l'affecte à la colonne et au compteur d'incrémentation automatique de la table. Si la table est vide, InnoDB utilise la valeur 1.

Donc, en clair, après le démarrage du service MySQL, il n'a aucune idée de la valeur d'incrémentation automatique de votre table. Ainsi, lorsque vous insérez une ligne pour la première fois, elle trouve la valeur maximale du champ qui utilise l'incrémentation automatique, ajoute 1 à cette valeur et utilise la valeur résultante. S'il n'y a pas de lignes, il commencera à 1.

C'était un problème pour nous, car nous utilisions la table et la fonction d'incrémentation automatique de mysql pour gérer proprement les identifiants dans un environnement multithread où les utilisateurs étaient redirigés vers un site de paiement tiers. Nous avons donc dû nous assurer que l'ID que le tiers avait obtenu et renvoyé était unique et resterait ainsi (et bien sûr, il est possible que l'utilisateur annule la transaction après avoir été redirigé).

Nous avons donc créé une ligne, obtenu la valeur d'auto-incrémentation générée, supprimé la ligne pour garder la table propre et transmis la valeur au site de paiement. Ce que nous avons fini par faire pour résoudre le problème de la façon dont InnoDB gère les valeurs AI était le suivant:

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

Cela conserve toujours le dernier transactionId généré sous forme de ligne dans la table, sans faire exploser inutilement la table.

J'espère que cela aide toute autre personne qui pourrait rencontrer cela.

Modifier (18/04/2018) :

Comme Finesse l'a mentionné ci-dessous, il semble que son comportement ait été modifié dans MySQL 8.0+.

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

Le libellé de ce journal de travail est au mieux défectueux, mais il semble qu'InnoDB dans ces versions plus récentes prend désormais en charge les valeurs autoinc persistantes à travers les redémarrages.

-Gremio


Pas mal pour un premier post, mec. +1.
ceejayoz

Douce connaissance là-bas Gremio. Merci!! J'avais complètement repoussé cela et archivé le problème en interne comme quelque chose à revoir à une date ultérieure, mais y revenir, votre solution fonctionne à merveille!
Hooligancat

3

Nous avons rencontré ce problème et avons découvert que lorsque la table d'optimisation était exécutée sur une table vide, la valeur d'incrémentation automatique était également réinitialisée. Voir ce rapport de bogue MySQL .

Pour contourner ce problème, vous pouvez:

ALTER TABLE a AUTO_INCREMENT=3 ENGINE=innoDB;

Au lieu de OPTIMIZE TABLE.

Il semble que c'est ce que fait MySQL en interne (sans définir la valeur d'incrémentation automatique, évidemment)


2

Juste un coup dans le noir - si l'application utilise un TRUNCATE TABLEpour vider la table une fois le traitement terminé, cela réinitialisera le champ d'incrémentation automatique. Voici une brève discussion sur la question. Bien que ce lien mentionne qu'InnoDB ne réinitialise pas les auto_increments sur un tronc, cela a été signalé comme un bug et corrigé il y a quelques années.

En supposant que ma supposition est juste, vous pouvez passer de la troncature à la suppression pour résoudre le problème.


Je ne pensais pas que nous utilisions TRUNCATE, mais nous avons dû vérifier pour nous en assurer. Même allé jusqu'à vérifier au niveau du pilote PHP pour vous en assurer. Mais nous ne l'étions pas. Appréciez cependant la solution possible.
Hooligancat

1

Seule une réinitialisation explicite de cette valeur, ou une suppression / recréation de ce champ, ou toute autre opération violente similaire devrait jamais réinitialiser un compteur auto_increment. (Le TRUNCATE était une très bonne théorie.) Il semble impossible que vous emballiez soudainement un INT 32 bits lorsque la dernière valeur dont vous êtes témoin n'est que de 600k. Il ne devrait certainement pas être réinitialisé simplement parce que la table se vide. Vous avez soit un bug mysql ou quelque chose dans votre code PHP. Ou le gars dans la cabine d'à côté vous joue un tour.

Vous pouvez déboguer en activant le journal binaire , car il contiendra des instructions comme celle-ci tout au long:

SET INSERT_ID=3747670/*!*/;

Ensuite, au moins, vous pouvez voir tous les détails de ce qui arrive à cette table, y compris juste avant la réinitialisation du compteur.


merci pour le conseil de débogage. Cela faisait un moment que je n'avais pas besoin d'enregistrer un défaut de serveur et j'apprécie votre conseil
Hooligancat

0

ALTER TABLE nom_table ENGINE = MyISAM

Fonctionne pour moi. Notre table est toujours très petite, donc pas besoin d'InnoDB.


Avez-vous besoin de transactions?
Anthony Rutledge

0

InnoDB ne stocke pas de valeur d'incrémentation automatique sur le disque, l'oublie donc lorsque le serveur MySQL est arrêté. Lorsque MySQL est démarré à nouveau, le moteur InnoDB restaure la valeur d'incrément automatique de cette façon: SELECT (MAX(id) + 1) AS auto_increment FROM table. Ceci est un bug qui est fixé dans la version MySQL 8.0.

Modifiez le moteur de table pour résoudre le problème:

ALTER TABLE table ENGINE = MyISAM

Ou mettez à jour le serveur MySQL vers la version 8.0 lors de sa sortie.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.