MySQL автоинкрементные поля сбрасываются самостоятельно

12

У нас есть таблица MySQL, в которой поле с автоинкрементом установлено как INT (11). Эта таблица в значительной степени хранит список заданий, которые выполняются в приложении. В любой момент времени на протяжении всего жизненного цикла приложения таблица может содержать тысячи записей или быть полностью пустой (т. Е. Все закончено).

Поле не привязано ни к чему другому.

Автоинкремент, по-видимому, случайным образом сбрасывает себя в ноль, хотя нам никогда не удавалось перехватить сброс.

Проблема становится очевидной, потому что мы видим, что поле автоинкремента достигает, скажем, 600 000 записей или около того, а затем, через некоторое время, поле автоинкремента, кажется, работает в нижних 1000.

Это почти как если бы автоинкремент сбрасывал себя, если таблица пуста.

Возможно ли это, и если да, то как его отключить или изменить способ сброса?

Если это не так, есть ли у кого-нибудь объяснение, почему он может это делать?

Благодарность!

Hooligancat
источник
Какая версия MySQL? Используются ли транзакции или репликация?
худой

Ответы:

26

Счетчик автоинкремента хранится только в основной памяти, а не на диске.

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

Из-за этого при перезапуске службы (или сервера) произойдет следующее:

После запуска сервера для первой вставки в таблицу t InnoDB выполняет эквивалент этого оператора: SELECT MAX (ai_col) FROM t FOR UPDATE;

InnoDB увеличивает на единицу значение, полученное оператором, и присваивает его столбцу и счетчику автоинкремента для таблицы. Если таблица пуста, InnoDB использует значение 1.

Таким образом, простым языком, после запуска службы MySQL он не знает, каким должно быть значение автоинкремента для вашей таблицы. Поэтому, когда вы впервые вставляете строку, он находит максимальное значение поля, которое использует автоинкремент, добавляет 1 к этому значению и использует полученное значение. Если строк нет, оно начнется с 1.

Это было проблемой для нас, так как мы использовали функцию автоматического увеличения таблицы и mysql для аккуратного управления идентификаторами в многопоточной среде, где пользователи перенаправлялись на сторонний платежный сайт. Таким образом, мы должны были удостовериться, что идентификатор, который третья сторона получила и отправила обратно нам, был уникальным и оставался таким (и, конечно, есть вероятность, что пользователь отменит транзакцию после того, как он будет перенаправлен).

Таким образом, мы создавали строку, получали сгенерированное значение автоинкремента, удаляли строку, чтобы поддерживать чистоту таблицы, и пересылали значение на сайт оплаты. В конечном итоге мы решили исправить проблему, с которой InnoDB обрабатывает значения AI:

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

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

Надеюсь, это поможет кому-то еще, кто может столкнуться с этим.

Изменить (2018-04-18) :

Как Finesse упоминается ниже, похоже, что поведение было изменено в MySQL 8.0+.

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

Формулировка в этом рабочем журнале в лучшем случае ошибочна, однако, похоже, что InnoDB в этих более новых версиях теперь поддерживает постоянные значения autoinc при перезагрузках.

-Gremio

Гремио
источник
Неплохо для первого поста, чувак. +1.
ceejayoz
Сладкое знание там Гремио. Благодарность!! Я полностью отложил это и заархивировал проблему внутренне как что-то, чтобы рассмотреть позже, но возвращаясь к этому, ваше решение работает очаровательно!
Hooligancat
3

Мы столкнулись с этой проблемой и обнаружили, что при запуске таблицы оптимизации для пустой таблицы значение автоинкремента также сбрасывалось. Смотрите этот отчет об ошибке MySQL .

В качестве обходного пути вы можете сделать:

ALTER TABLE a AUTO_INCREMENT=3 ENGINE=innoDB;

Вместо OPTIMIZE TABLE.

Кажется, это то, что делает MySQL внутренне (очевидно, без установки значения автоинкремента)

прут
источник
2

Просто выстрел в темноте - если приложение использует TRUNCATE TABLEдля очистки таблицы после завершения обработки, это приведет к сбросу поля автоинкремента. Вот краткое обсуждение вопроса. Хотя в этой ссылке упоминается, что InnoDB не сбрасывает auto_increments для усечения, это было сообщено как ошибка и исправлено несколько лет назад.

Если предположить, что мои предположения верны, вы можете перейти от усечения к удалению, чтобы устранить проблему.

dsolimano
источник
Я не думал, что мы использовали TRUNCATE, но мы должны были проверить, чтобы убедиться. Даже зашел так далеко, что проверил до уровня драйвера PHP, чтобы убедиться. Но мы не были. Оцените возможное решение, хотя.
Hooligancat
1

Только явный сброс этого значения, или удаление / воссоздание этого поля, или другие подобные насильственные операции должны когда-либо сбрасывать счетчик auto_increment. (TRUNCATE была действительно хорошей теорией.) Кажется невозможным, чтобы вы внезапно обернули 32-битный INT, когда последнее значение, которое вы видите, составляет всего 600k. Это определенно не должно сбрасываться только потому, что таблица пуста. У вас либо ошибка MySQL, либо что-то в вашем PHP-коде. Или парень в соседней кабинке подшучивает над тобой.

Вы можете отладить, включив двоичный журнал , так как он будет содержать такие выражения:

SET INSERT_ID=3747670/*!*/;

Тогда, по крайней мере, вы сможете увидеть все детали того, что происходит с этим столом, в том числе прямо перед сбросом счетчика.

IcarusNM
источник
спасибо за подсказку отладки. Прошло много времени с тех пор, как мне нужно было проверить на Serverfault, и я ценю ваш совет
Hooligancat
0

ALTER TABLE table_name ENGINE = MyISAM

Работает на меня. Наш стол всегда очень маленький, поэтому нет необходимости в InnoDB.

Быстрая починка
источник
Вам нужны транзакции?
Энтони Ратледж
0

InnoDB не сохраняет значение автоинкремента на диске, поэтому забывает его, когда сервер MySQL выключен. Когда MySQL запускается снова, двигатель InnoDB восстанавливает значение автоматическое приращение следующим образом: SELECT (MAX(id) + 1) AS auto_increment FROM table. Это ошибка , которая фиксируется в MySQL версии 8.0.

Измените движок стола, чтобы решить проблему:

ALTER TABLE table ENGINE = MyISAM

Или обновите сервер MySQL до версии 8.0, когда он будет выпущен.

тонкость
источник