Impida el restablecimiento de la identificación de aumento automático en la base de datos Innodb después de reiniciar el servidor

11

Recientemente leí que debido a cómo InnoDB recalcula el valor de AUTO_INCREMENT cuando el servidor se reinicia, cualquier registro en el extremo superior de la lista de ID puede volver a usar sus ID.

Normalmente, esto no es un problema, porque cuando se elimina un usuario, todo lo relacionado con la ID también se elimina de otras tablas.

Pero dejo deliberadamente sus publicaciones en el foro huérfanas, etiquetadas como "Publicado por = Usuario # 123 =", para que se retengan las conversaciones pasadas. Claramente, si se reutiliza una identificación, esto será un problema.

Nunca he tenido este problema antes porque siempre había suficientes usuarios nuevos para que sea poco probable que una ID se reutilice de esta manera. Sin embargo, en mi nuevo proyecto, las inscripciones son raras y las eliminaciones de usuarios inactivas son frecuentes (especialmente dado que las cuentas "Open Alpha" solo duran tres días como una vista previa), y dicha reutilización de ID ha sucedido tres por tres ahora.

He "solucionado" el problema guardando el valor correcto para AUTO_INCREMENT en otro lugar y utilizándolo en lugar de confiar en el valor interno. ¿Hay alguna forma real de que InnoDB recuerde el último valor real?

Naveen Kumar
fuente
¿Tienes el artículo que lees?
gbn
@gbn El enlace para el artículo dev.mysql.com/doc/refman/5.1/en/…
Naveen Kumar
Para la referencia esto es bugs.mysql.com/bug.php?id=199
Laurynas Biveinis
ALTER TABLE table_name ENGINE = MyISAM Funciona para mí. Nuestra mesa siempre se mantiene muy pequeña, por lo que no necesita InnoDB.
1
@QuickFix Debe agregar algunos detalles sobre por qué esto funciona.
Max Vernon

Respuestas:

5

(evitando el problema al no eliminar nunca)

Como desea conservar la "Posted by =User #123="información después de eliminar al usuario id=123, también puede considerar usar 2 tablas para almacenar los datos de los usuarios. Uno para Activeusuarios y uno para todos (incluidos los eliminados de los usuarios activos). Y nunca elimine esos identificadores de la AllUsertabla:

CREATE TABLE AllUser
( user_id INT AUTO_INCREMENT
, ...
, PRIMARY KEY (user_id)
) ;

------
--- Forum posts FK should reference the `AllUser` table

CREATE TABLE ActiveUser
( user_id INT 
, ...
, PRIMARY KEY (user_id)
, FOREIGN KEY (user_id)
    REFERENCES AllUser (user_id)
) ;

------
--- All other FKs should reference the `ActiveUser` table

Por supuesto, esto complicará la operación de insertar un nuevo usuario. Cualquier usuario nuevo significará 2 inserciones, una en cada tabla. Sin embargo, la eliminación de un usuario se realizará ActiveUsersolo en la tabla. Todos los FK se eliminarán en cascada, excepto las publicaciones del foro, que harán referencia a la Allusertabla (donde nunca se eliminará).

ypercubeᵀᴹ
fuente
4

No hay una forma natural de hacer esto, excepto usar information_schema.tables para registrar todas las columnas con la opción auto_increment.

Puede recopilar esas columnas de la siguiente manera:

CREATE TABLE mysql.my_autoinc ENGINE=MyISAM
SELECT table_schema,table_name,auto_increment
FROM information_schema.tables WHERE 1=2;
ALTER TABLE mysql.my_autoinc ADD PRIMARY KEY (table_schema,table_name);
INSERT INTO mysql.my_autoinc
SELECT table_schema,table_name,auto_increment
FROM information_schema.tables WHERE auto_increment IS NOT NULL;

Cree un script que restablecerá los valores de auto_increment

AUTOINC_SCRIPT=/var/lib/mysql/ResetAutoInc.sql
mysql -u... -p... -AN -e"SELECT CONCAT('ALTER TABLE ',table_schema,'.',table_name,' AUTO_INCREMENT=',auto_increment,';') FROM mysql.my_autoinc" > ${AUTOINC_SCRIPT}

Entonces podría hacer una de dos cosas:

OPCIÓN # 1: Ejecutar script manualmente después del inicio

mysql> source /var/lib/mysql/ResetAutoInc.sql

OPCIÓN # 2: haga que mysqld ejecute el script antes de permitir conexiones

Tendrías que agregar esta opción

[mysqld]
init-file=/var/lib/mysql/ResetAutoInc.sql

De esa manera, cada vez que reinicie mysql, este script se ejecuta al principio. Tendrá que recordar regenerar /var/lib/mysql/ResetAutoInc.sql antes de hacer un reinicio planificado de mysql.

RolandoMySQLDBA
fuente
3

Los 5.5 documentos sugieren almacenar el valor de incremento automático en otro lugar como ya lo ha hecho.

Una solución alternativa sería emular una SECUENCIA para que no use el incremento automático en la tabla en sí. Esto ha sido discutido en SO antes y otra vez . El blog MySQL Performance lo menciona.

Sin embargo, otra información de MySQL atornilla que otros RDBMS no tienen ...

gbn
fuente
2

Simplemente no elimines al usuario. La integridad relacional es más importante. Si tiene que hacerlo por razones de privacidad o lo que sea, simplemente cambie el nombre de usuario a 'eliminado' y borre cualquier otro campo.

Jannes
fuente
1

Esta es una vieja pregunta y sigue siendo relevante.

1) Este comportamiento se está reparando en Mysql 8.0.

2) Una solución es usar una fila ficticia para sus datos, para mantener el AUTO_INCREMENT por encima de un cierto valor. No es muy conveniente dependiendo de lo que esté almacenando, pero es una solución simple en algunos casos.

Garr Godfrey
fuente
0

Necesitábamos una solución extrapolada para nuestro propio sistema basada en las instrucciones de esta publicación. Si esto puede ayudar a alguien a alcanzar su objetivo de una manera aún más fácil.

Nuestro sistema utiliza un patrón de tabla de lápidas para almacenar elementos eliminados porque hacemos una sincronización bidireccional en los sistemas desconectados, por lo que utilizamos este código para hacer coincidir las tablas de lápidas con sus tablas en vivo y extraer el mayor valor posible :)

DROP PROCEDURE IF EXISTS `reset_auto_increments`;
DELIMITER $
CREATE PROCEDURE reset_auto_increments()
BEGIN

    DECLARE done INT DEFAULT 0;
    DECLARE schemaName VARCHAR(255) DEFAULT '';
    DECLARE liveTableName VARCHAR(255) DEFAULT '';
    DECLARE tombstoneTableName VARCHAR(255) DEFAULT '';
    DECLARE liveAutoIncrement INT DEFAULT 0;
    DECLARE tombstoneAutoIncrement INT DEFAULT 0;
    DECLARE newAutoIncrement INT DEFAULT 0;

    DECLARE autoIncrementPairs CURSOR FOR 
        SELECT
            liveTables.TABLE_SCHEMA AS schemaName,
            liveTables.TABLE_NAME AS liveTable, 
            tombstoneTables.TABLE_NAME AS tombstoneTable,
            liveTables.AUTO_INCREMENT AS live_auto_increment,
            tombstoneTables.AUTO_INCREMENT AS tombstone_auto_increment,
            GREATEST(liveTables.AUTO_INCREMENT, tombstoneTables.AUTO_INCREMENT) AS new_auto_increment
        FROM 
            information_schema.tables AS liveTables
            JOIN information_schema.tables AS tombstoneTables
                ON liveTables.TABLE_SCHEMA = tombstoneTables.TABLE_SCHEMA
                    AND CONCAT('deleted', UCASE(LEFT(liveTables.TABLE_NAME, 1)), SUBSTRING(liveTables.TABLE_NAME, 2))
                        = tombstoneTables.TABLE_NAME
        WHERE
            GREATEST(liveTables.AUTO_INCREMENT, tombstoneTables.AUTO_INCREMENT) IS NOT NULL;

    DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;

    SET done = 0;

    SET schemaName = '';
    SET liveTableName = '';
    SET tombstoneTableName = '';
    SET liveAutoIncrement = 0;
    SET tombstoneAutoIncrement = 0;
    SET newAutoIncrement = 0;

    OPEN autoIncrementPairs;
    REPEAT

        FETCH autoIncrementPairs INTO 
            schemaName, 
            liveTableName, 
            tombstoneTableName, 
            liveAutoIncrement, 
            tombstoneAutoIncrement, 
            newAutoIncrement;

        SET @statement = CONCAT('ALTER TABLE ', schemaName, '.', liveTableName, ' AUTO_INCREMENT=', newAutoIncrement);
        PREPARE updateAutoIncrementStatement FROM @statement;
        EXECUTE updateAutoIncrementStatement;
        DEALLOCATE PREPARE updateAutoIncrementStatement;

    UNTIL done END REPEAT;

    CLOSE autoIncrementPairs;

END$

DELIMITER ;
Mathieu Dumoulin
fuente