Запретить сброс идентификатора auto_increment в базе данных Innodb после перезапуска сервера


11

Недавно я читал, что из-за того, как InnoDB пересчитывает значение AUTO_INCREMENT при перезапуске сервера, любые записи в верхнем конце списка идентификаторов могут повторно использоваться их идентификаторы.

Обычно это не проблема, поскольку при удалении пользователя все, что связано с идентификатором, удаляется и из других таблиц.

Но я намеренно оставляю их сообщения на форуме осиротевшими, помеченными как «Автор: = Пользователь # 123 =», так что прошлые разговоры сохраняются. Очевидно, что в случае повторного использования идентификатора это будет проблемой.

У меня никогда не было этой проблемы раньше, потому что всегда было достаточно новых пользователей, чтобы маловероятно, что идентификатор будет использован таким образом. Однако в моем новом проекте регистрации происходят редко, а неактивные удаления пользователей происходят часто (особенно потому, что учетные записи «Open Alpha» длятся только три дня в качестве предварительного просмотра), и такое повторное использование идентификаторов уже произошло три на три.

Я «исправил» проблему, сохранив правильное значение для AUTO_INCREMENT в другом месте и используя его вместо того, чтобы полагаться на внутреннее значение. Есть ли реальный способ, чтобы InnoDB запомнил фактическое последнее значение?


У вас есть статья, которую вы прочитали?
2012 года

@gbn Ссылка на статью dev.mysql.com/doc/refman/5.1/en/…
Навин Кумар


ALTER TABLE table_name ENGINE = MyISAM У меня работает. Наш стол всегда очень маленький, поэтому нет необходимости в InnoDB.

1
@ QuickFix Вы должны добавить некоторые подробности о том, почему это работает.
Макс Вернон

Ответы:


5

(избегая проблемы, никогда не удаляя)

Поскольку вы хотите сохранить "Posted by =User #123="информацию после удаления пользователя id=123, вы также можете рассмотреть возможность использования двух таблиц для хранения данных пользователей. Один для Activeпользователей и один для всех (включая удаленных из активных пользователей). И никогда не удаляйте эти идентификаторы из AllUserтаблицы:

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

Это, конечно, усложнит операцию вставки нового пользователя. Любой новый пользователь будет означать 2 вставки, по одной в каждой таблице. Удаление пользователя будет происходить только путем удаления из ActiveUserтаблицы. Все FK будут удалены каскадно, кроме сообщений на форуме, которые будут ссылаться на Alluserтаблицу (где удаление никогда не произойдет).


4

Нет естественного способа сделать это, кроме как использовать information_schema.tables для записи всех столбцов с опцией auto_increment.

Вы можете собрать эти столбцы следующим образом:

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;

Создайте скрипт, который будет сбрасывать значения 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}

Затем вы можете сделать одну из двух вещей:

ВАРИАНТ №1: запускать скрипт вручную после запуска

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

ВАРИАНТ № 2: Пусть mysqld выполняет скрипт перед разрешением соединений

Вы должны добавить эту опцию

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

Таким образом, каждый раз, когда вы перезапускаете mysql, этот скрипт выполняется в начале. Перед выполнением запланированного перезапуска mysql вам придется помнить о регенерации /var/lib/mysql/ResetAutoInc.sql.


3

В документации 5.5 предлагается хранить значение автоинкремента в другом месте, как у вас уже есть.

Альтернативным решением будет эмуляция SEQUENCE, чтобы вы не использовали автоинкремент в самой таблице. Это обсуждалось на SO до и снова . Блоге MySQL Performance упоминает о нем.

Еще одна ошибка в данных MySQL, которой нет в других СУБД ...


2

Только не удаляйте пользователя. Относительная целостность важнее. Если вам нужно из-за соображений конфиденциальности или чего-то еще, просто измените имя пользователя на «удалено» и удалите все остальные поля.


1

Это старый вопрос и по-прежнему актуален.

1) Это поведение исправлено в Mysql 8.0.

2) Одним из решений является использование фиктивной строки для ваших данных, чтобы значение AUTO_INCREMENT превышало определенное значение. Не очень удобно в зависимости от того, что вы храните, но в некоторых случаях это простое решение.


0

Нам понадобилось это экстраполированное решение для нашей собственной системы, основанное на инструкциях на этом посте. Если это может помочь кому-то достичь своей цели еще более простым способом.

Наша система использует шаблон таблиц-надгробий для хранения удаленных элементов, потому что мы выполняем двухстороннюю синхронизацию на отключенных системах, поэтому мы используем этот код для сопоставления таблиц-надгробий с их действующими таблицами и извлечения максимально возможного значения :)

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 ;
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.