Los campos de incremento automático de MySQL se restablecen por sí mismos

12

Tenemos una tabla MySQL que tiene un campo de incremento automático establecido como INT (11). Esta tabla prácticamente almacena una lista de trabajos que se ejecutan en una aplicación. En cualquier momento durante la vida útil de la aplicación, la tabla podría contener miles de entradas o estar completamente vacía (es decir, todo ha terminado).

El campo no tiene una clave externa para nada más.

El incremento automático parece reiniciarse aleatoriamente a cero, aunque en realidad nunca hemos podido atrapar el reinicio.

El problema se hace evidente porque vemos que el campo de incremento automático llega a, digamos, 600,000 registros más o menos, y luego, un tiempo después, el campo de incremento automático parece ejecutarse en los 1000 bajos.

Es casi como si el incremento automático se reiniciara si la tabla está vacía.

¿Es esto posible y, si es así, cómo lo apago o cambio la manera en que se restablece?

Si no es así, ¿alguien tiene una explicación de por qué podría estar haciendo esto?

¡Gracias!

Hooligancat
fuente
¿Qué versión de MySQL? ¿Están en uso las transacciones o la replicación?
thinice

Respuestas:

26

El contador de incremento automático se almacena solo en la memoria principal, no en el disco.

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

Debido a esto, cuando el servicio (o servidor) se reinicia, sucederá lo siguiente:

Después del inicio del servidor, para la primera inserción en una tabla t, InnoDB ejecuta el equivalente de esta instrucción: SELECT MAX (ai_col) FROM t FOR UPDATE;

InnoDB incrementa en uno el valor recuperado por la instrucción y lo asigna a la columna y al contador de incremento automático para la tabla. Si la tabla está vacía, InnoDB usa el valor 1.

Entonces, en inglés simple, después de que se inicia el servicio MySQL, no tiene idea de cuál debería ser el valor de incremento automático para su tabla. Entonces, cuando inserta una fila por primera vez, encuentra el valor máximo del campo que usa el incremento automático, agrega 1 a este valor y usa el valor resultante. Si no hay filas, comenzará en 1.

Esto fue un problema para nosotros, ya que estábamos usando la tabla y la función de autoincremento de mysql para administrar perfectamente las identificaciones en un entorno de subprocesos múltiples donde los usuarios se redirigían a un sitio de pago de terceros. Así que teníamos que asegurarnos de que la identificación que el tercero nos envió y nos devolvió fuera única y que se mantuviera así (y, por supuesto, existe la posibilidad de que el usuario cancele la transacción después de haber sido redirigido).

Así que estábamos creando una fila, obteniendo el valor de incremento automático generado, eliminando la fila para mantener limpia la tabla y reenviando el valor al sitio de pago. Lo que terminamos haciendo para solucionar el problema de la forma en que InnoDB maneja los valores de AI fue lo siguiente:

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

Esto siempre mantiene el último Id. De transacción generado como una fila en la tabla, sin explotar innecesariamente la tabla.

Espero que ayude a cualquier otra persona que pueda encontrarse con esto.

Editar (2018-04-18) :

Como Finesse menciona a continuación, parece que el comportamiento de esto se ha modificado en MySQL 8.0+.

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

La redacción de ese registro de trabajo es, en el mejor de los casos, defectuosa, sin embargo, parece que InnoDB en esas versiones más nuevas ahora admite valores de inclusión automática persistentes en todos los reinicios.

-Gremio

Gremio
fuente
No está mal para una primera publicación, amigo. +1.
ceejayoz
Dulce conocimiento allí Gremio. ¡¡Gracias!! Lo había pospuesto por completo y archivé el problema internamente como algo para revisar en una fecha posterior, pero volviendo a ello, ¡su solución funciona a las mil maravillas!
Hooligancat
3

Hemos experimentado este problema y hemos descubierto que cuando la tabla de optimización se ejecutó en una tabla vacía, el valor de incremento automático también se restableció. Vea este informe de error de MySQL .

Como solución alternativa, puede hacer:

ALTER TABLE a AUTO_INCREMENT=3 ENGINE=innoDB;

En lugar de OPTIMIZE TABLE.

Parece que esto es lo que MySQL hace internamente (sin establecer el valor de incremento automático, obviamente)

varilla
fuente
2

Solo un disparo en la oscuridad: si la aplicación está utilizando a TRUNCATE TABLEpara vaciar la tabla cuando termine de procesar, eso restablecerá el campo de incremento automático. Aquí hay una breve discusión sobre la cuestión. Aunque ese enlace menciona que InnoDB no restablece los aumentos automáticos en un trunc, eso se informó como un error y se solucionó hace unos años.

Suponiendo que mi suposición es correcta, puede cambiar de truncar a eliminar para solucionar el problema.

dsolimano
fuente
No pensé que estábamos usando TRUNCATE, pero tuvimos que verificar para asegurarnos. Incluso fue tan lejos como para verificar hasta el nivel del controlador PHP para asegurarse. Pero no lo estábamos. Sin embargo, aprecio la posible solución.
Hooligancat
1

Solo un restablecimiento explícito de ese valor, o una caída / recreación de ese campo u otra operación violenta similar debería restablecer un contador de aumento automático. (El TRUNCATE fue una muy buena teoría.) Parece imposible que de repente estés envolviendo un INT de 32 bits cuando el último valor que presencias es solo 600k. Definitivamente no debería reiniciarse solo porque la tabla se vacía. O tienes un error mysql o algo en tu código PHP. O el chico del cubículo de al lado te está engañando.

Puede depurar activando el registro binario , ya que contendrá declaraciones como esta en todo momento:

SET INSERT_ID=3747670/*!*/;

Entonces, al menos, puede ver cada detalle de lo que está sucediendo en esa tabla, incluso justo antes de que se reinicie el contador.

IcarusNM
fuente
Gracias por el consejo de depuración. Ha pasado un tiempo desde que necesitaba registrar un Serverfault y agradezco su sugerencia
Hooligancat
0

ALTER TABLE table_name ENGINE = MyISAM

Funciona para mi. Nuestra mesa siempre se mantiene muy pequeña, por lo que no necesita InnoDB.

Arreglo rapido
fuente
¿Necesitas transacciones?
Anthony Rutledge
0

InnoDB no almacena el valor de incremento automático en el disco, por lo que lo olvida cuando se cierra el servidor MySQL. Cuando el MySQL se inicia de nuevo, el motor InnoDB restaura el valor de incremento automático de esta manera: SELECT (MAX(id) + 1) AS auto_increment FROM table. Este es un error que se corrige en MySQL versión 8.0.

Cambie el motor de la tabla para resolver el problema:

ALTER TABLE table ENGINE = MyISAM

O actualice el servidor MySQL a la versión 8.0 cuando se lance.

Finura
fuente