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

11

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

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

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

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

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

Навин Кумар
источник
У вас есть статья, которую вы прочитали?
2012 года
@gbn Ссылка на статью dev.mysql.com/doc/refman/5.1/en/…
Навин Кумар
Для справки: bugs.mysql.com/bug.php?id=199
Лауринас Бивейнис,
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таблицу (где удаление никогда не произойдет).

ypercubeᵀᴹ
источник
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.

RolandoMySQLDBA
источник
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 ;
Матье Дюмулен
источник