Ce comportement est documenté (paragraphe entre parenthèses) :
Si vous spécifiez ON DUPLICATE KEY UPDATE et qu'une ligne est insérée, ce qui entraînerait une valeur en double dans un index UNIQUE ou une CLÉ PRIMAIRE, MySQL effectue une MISE À JOUR de l'ancienne ligne. Par exemple, si la colonne a est déclarée UNIQUE et contient la valeur 1, les deux instructions suivantes ont un effet similaire :
INSERT INTO table (a,b,c) VALUES (1,2,3) ON DUPLICATE KEY UPDATE c=c+1; UPDATE table SET c=c+1 WHERE a=1;
(Les effets ne sont pas identiques pour une table InnoDB où a est une colonne à incrémentation automatique. Avec une colonne à incrémentation automatique, une instruction INSERT augmente la valeur d'incrémentation automatique, mais pas UPDATE.)
Voici une explication simple. MySQL tente de faire l'insertion en premier. C'est à ce moment que l'identifiant est automatiquement incrémenté. Une fois incrémenté, il reste. Ensuite, le doublon est détecté et la mise à jour se produit. Mais la valeur est manquée.
Vous ne devez pas dépendre de auto_increment
n'ayant pas de lacunes. S'il s'agit d'une exigence, la surcharge des mises à jour et des insertions est beaucoup plus importante. Essentiellement, vous devez mettre un verrou sur toute la table et renuméroter tout ce qui doit être renuméroté, généralement à l'aide d'un déclencheur. Une meilleure solution consiste à calculer des valeurs incrémentielles en sortie.