Este comportamiento está documentado (párrafo entre paréntesis):
Si especifica EN ACTUALIZACIÓN DE CLAVE DUPLICADA, y se inserta una fila que causaría un valor duplicado en un índice ÚNICO o CLAVE PRINCIPAL, MySQL realiza una ACTUALIZACIÓN de la fila anterior. Por ejemplo, si la columna a se declara como ÚNICA y contiene el valor 1, las siguientes dos declaraciones tienen un efecto similar:
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;
(Los efectos no son idénticos para una tabla InnoDB donde a es una columna de incremento automático. Con una columna de incremento automático, una instrucción INSERT aumenta el valor de incremento automático pero UPDATE no).
Aquí hay una explicación simple. MySQL intenta hacer la inserción primero. Aquí es cuando la identificación se incrementa automáticamente. Una vez incrementado, se mantiene. Luego se detecta el duplicado y ocurre la actualización. Pero el valor se pierde.
No debe depender de auto_increment
no tener lagunas. Si ese es un requisito, la sobrecarga de las actualizaciones y las inserciones es mucho mayor. Esencialmente, debe poner un candado en toda la tabla y volver a numerar todo lo que debe volver a numerarse, generalmente usando un activador. Una mejor solución es calcular valores incrementales en la salida.