Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > MySQL > Непонятное поведение auto_increment


Автор: HeliosArt 10.11.2010, 16:08
Столкнулся со странностью работы auto_increment в xtradb-таблице. Суть в следующем:

Есть две таблицы:
Код

CREATE TABLE `attribute_set` (
    `attribute_set_id` smallint(5) UNSIGNED NOT NULL AUTO_INCREMENT,
    `attribute_set_name` varchar(255 NOT NULL DEFAULT '',
    `sort_order` smallint(6) NOT NULL DEFAULT '0',
    PRIMARY KEY (`attribute_set_id`)
) ENGINE=`InnoDB` AUTO_INCREMENT=828 DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci ROW_FORMAT=COMPACT CHECKSUM=0 DELAY_KEY_WRITE=0;

и
Код

CREATE TABLE `attribute_group` (
    `attribute_group_id` smallint(5) UNSIGNED NOT NULL AUTO_INCREMENT,
    `attribute_set_id` smallint(5) UNSIGNED NOT NULL DEFAULT '0',
    `attribute_group_name` varchar(255) NOT NULL DEFAULT '',
    `sort_order` smallint(6) NOT NULL DEFAULT '0',
    PRIMARY KEY (`attribute_group_id`),
    CONSTRAINT `FK_eav_attribute_group` FOREIGN KEY (`attribute_set_id`) REFERENCES `attribute_set` (`attribute_set_id`)   ON UPDATE CASCADE ON DELETE CASCADE,
    UNIQUE `attribute_set_id`(attribute_set_id, attribute_group_name)
) ENGINE=`InnoDB` AUTO_INCREMENT=0 DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci ROW_FORMAT=COMPACT CHECKSUM=0 DELAY_KEY_WRITE=0;


В первой таблице хранится 827 записей. Мне нужно вставить во вторую таблицу по 2 записи на каждую запись в первой.
Для этого выполняю 2 идентичных запроса:
Код

INSERT INTO `attribute_group` (`attribute_set_id`, `attribute_group_name`) 
    SELECT `attribute_set_id`, 'Общие данные' FROM `attribute_set` WHERE 1;

и
Код

INSERT INTO `attribute_group` (`attribute_set_id`, `attribute_group_name`) 
    SELECT `attribute_set_id`, 'Meta-данные' FROM `attribute_set` WHERE 1;


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

В итоге после первого запроса в таблице attribute_group появляется 827 записей с id от 1 до 827.
После второго запроса также добавляется 827 записей, но их id начинаются не с 828, как ожидалось, а с 1024.

Первой мыслью было то, что такая работа - это какой-то патч для повышения производительности в самой xtradb - слишком уж 1024 похоже на какое-то двоичное выравнивание. Но на поверку оказалось, что в стандартном innodb ситуация такая же.

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

Автор: skyboy 10.11.2010, 16:42
сходу ничего не нашел. кроме самого факт, что воспроизводится проблема-то.

Автор: Akina 10.11.2010, 17:00
Я не понял... а какое собсно твоё дело, какой автоинкремент сгенерён? он не для работы, а только для обеспечения целостности и связей.

Автор: HeliosArt 10.11.2010, 17:07
Цитата(Akina @ 10.11.2010,  15:00)
Я не понял... а какое собсно твоё дело, какой автоинкремент сгенерён? он не для работы, а только для обеспечения целостности и связей.

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

ЗЫ: Как я уже сказал, баг не критичен, но это не повод приходить и грубить в топике, в котором вы не знаете что ответить.

Автор: skyboy 10.11.2010, 17:08
Akina, ты знаешь причину "пропусков"?

Добавлено через 35 секунд
Цитата(HeliosArt @  10.11.2010,  16:07 Найти цитируемый пост)
но это не повод приходить и грубить в топике

спокойно. никто не грубит.

Добавлено через 2 минуты и 6 секунд
Цитата(Akina @  10.11.2010,  16:00 Найти цитируемый пост)
а какое собсно твоё дело, какой автоинкремент сгенерён?

зависит от причин этих проявлений.
возможно, это симптомы бага/фичи, из-за которых не стоит использовать конструкцию insert ... select. к примеру.

Автор: Akina 10.11.2010, 18:46
Цитата(skyboy @  10.11.2010,  18:08 Найти цитируемый пост)
ты знаешь причину "пропусков"?

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

Цитата(HeliosArt @  10.11.2010,  18:07 Найти цитируемый пост)
мне не очень комфортно работать с системами, логика работы которых мне не ясна. Вот я эту логику и пытаюсь узнать и понять.

Берёшь исходник и смотришь, что там разработчики наворочали. Если возникает разрыв - значит, выполняются некие процессы, которые инкрементируют счётчик, но не приводят к появлению записей в таблице.

Цитата(HeliosArt @  10.11.2010,  18:07 Найти цитируемый пост)
баг не критичен

Это не баг. Если тебе нужна нумерация строго по порядку - заведи ещё одно поле и нумеруй его без дырок, расчётно или триггером. А синтетический ключ оставь системе для её нужд.
Или перейди на использование естественного ключа. Но тогда это однозначно будет не автоинкремент.

Цитата(skyboy @  10.11.2010,  18:08 Найти цитируемый пост)
возможно, это симптомы бага/фичи, из-за которых не стоит использовать конструкцию insert ... select. к примеру. 

"Не верю!" (с)

Автор: Zloxa 11.11.2010, 13:05
Сразу(в очередной раз) скажу, в MySQL не шарю, но гипотезу таки изложу, ибо скучаю.

InnDB, в отличии от MyISAM, если я не путаю, позволяет конкурентный доступ к таблице. Соответственно должен както обеспечивать возможности одновременной вставки несколькими процессами. Однако, что если таблица имеет автоинкрементное поле? Для двух вставляющих процессов это будет разделяемый ресурс. Если мы будем наращивать счетчик по единичке, на этом ресурсе будут постоянно толкаться две конкурирующие сессии и никакого профита от паралеллелизма мы не увидим. Потому, многие системы наращивают счетчики не на еденичку,  забирают под сессию/*транзакцию*/ сразу некий диапазон, как только диапазон выработался, забирают новый. Это позволяет более эффективно испльзовать разделяемый ресурс. В оракле размер кэша последовательности - настраиваемый параметр.
Думаю, проявление этого, или же чегото сродни, и предстало перед нашими очами.

Автор: Akina 11.11.2010, 13:29
В соответствии с документацией
Цитата

The AUTO_INCREMENT attribute can be used to generate a unique identity for new rows.

Т.е. УНИКАЛЬНЫЙ. И не более.

Можете обрыть всю документацию - но нигде не удастся применительно к нему найти слово ПОСЛЕДОВАТЕЛЬНЫЙ (consecutive, cascade, consistent, sequential, serial и т.п.). И это относится не только к MySQL - то же будет и для любого другого сервера БД.

Автор: Zloxa 11.11.2010, 13:40
Akina, у меня создается ощущение что ТС не настаивает на том, что MySQL обязан ему выдать непрерывную последовательность. Ему, мне кажется, лишь интересны причины, обуславливающие ее прерывистость.

Добавлено через 58 секунд
А холивор на тему обеспечения бездырочной нумирации, таки да, уже приелся. Но тут, мне кажется, не тот случай. smile

Автор: Akina 11.11.2010, 14:02
Цитата(Zloxa @  11.11.2010,  14:40 Найти цитируемый пост)
Ему, мне кажется, лишь интересны причины, обуславливающие ее прерывистость.

Но если его при этом не устраивает версия "by design" - то есть два пути. Либо рыть самому, либо искать результаты того, кто рыл до него. Первое ему не нравится, кажется, а что до второго... я лично видел такие копания, но где именно - не помню. Да и было это давно, по-моему, ещё аж для 3-й версии... 

Автор: Zloxa 11.11.2010, 14:08
Akina, ИМХО, ТС лишь погорячился использовать слово "баг". Меня тоже это малость покоробило, но, видать у меня настрой сегодня несколько добродушный, я на это слово не саггрился. smile

Нак бы там ни было, думаю надо таки навести статускво
Цитата(HeliosArt @  10.11.2010,  17:07 Найти цитируемый пост)
баг 

http://segfault.kiev.ua/smart-questions-ru.html#itsabug

Автор: HeliosArt 11.11.2010, 14:10
Да, сам факт того, что пропущены ID меня мало волнует. Интересны только причины такого нестандартного поведения.

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

Версия by design меня бы устроила, если бы база так вела себя всегда. Тут же получается, что случай нестандартный, иначе на него уже бы давно обратили внимание.

Этот топик как раз и создан чтобы узнать, рыл ли кто-то ранее и что узнали в результате.

Цитата

Akina, ИМХО, ТС лишь погорячился использовать слово "баг".

Так и есть smile

Автор: Zloxa 11.11.2010, 14:15
Цитата(HeliosArt @  11.11.2010,  14:10 Найти цитируемый пост)
случай нестандартный, иначе на него уже бы давно обратили внимание.

не думаю.
большинство разработчиков БД, как уже верно заметил Akina,  озабочено обеспечением уникальности первичного ключа.
Его непрерывностью озабочены лишь пытливые мозги ищущих новичков. Да и то - далеко не всех. smile

Я вот с ностальгией вспоминаю те полные наива времена, когда еще на фоксе, в девяностые годы, на производственной практике бился с обеспечением непрерывности нумерации ключа при удалении.

Цитата(HeliosArt @  11.11.2010,  14:10 Найти цитируемый пост)
Предположение о кеше транзакции тут вряд ли подойдет, т.к. запросы выполнялись в рамках одной транзакции.

ну хрен сним, заменим слово "транзакция" на "стейтмент"(или как биш оно на руский то переводится) smile
что это меняет?

Автор: Akina 11.11.2010, 16:03
Цитата(HeliosArt @  11.11.2010,  15:10 Найти цитируемый пост)
Тут же получается, что случай нестандартный, иначе на него уже бы давно обратили внимание.

И что в нём нестандартного? как известно, всё, что не описано явно, имеет право быть как угодно, и каждый раз как угодно как угодно.

Автор: HeliosArt 11.11.2010, 17:06
Цитата(Akina @ 11.11.2010,  14:03)
Цитата(HeliosArt @  11.11.2010,  15:10 Найти цитируемый пост)
Тут же получается, что случай нестандартный, иначе на него уже бы давно обратили внимание.

И что в нём нестандартного? как известно, всё, что не описано явно, имеет право быть как угодно, и каждый раз как угодно как угодно.

Согласно http://dev.mysql.com/doc/refman/5.1/en/innodb-auto-increment-handling.html:

Цитата

After the auto-increment counter has been initialized, if a you do not explicitly specify a value for an AUTO_INCREMENT column, InnoDB increments the counter and assigns the new value to the column.

....

By default, the value is incremented by one. This default can be overridden by the auto_increment_increment configuration setting.

auto_increment_increment controls the interval between successive column values.

...

InnoDB uses a special lock called the table-level AUTO-INC lock for inserts into tables with AUTO_INCREMENT columns. This lock is normally held to the end of the statement (not to the end of the transaction), to ensure that auto-increment numbers are assigned in a predictable and repeatable order for a given sequence of INSERT statements.


Т.е. в документации механизм работы auto_increment описан явно и "каждый раз как угодно как угодно." тут не прокатит.

Причина же пропуска нашлась, и догадка Zloxa оказалась верной, ибо среди кучи текста о последовательности и предсказуемости нашлось следующее:
Цитата

For lock modes 1 or 2, gaps may occur between successive statements because for bulk inserts the exact number of auto-increment values required by each statement may not be known and overestimation is possible.


Только при этом логика разработчиков мне все равно не ясна: если уж AUTO-INC lock висит на таблице до завершения всего statement'а и в ходе запроса новые значения выделяются row-by-row, то почему по завершении не вписать в счетчик последнее использованное значение? Для этого даже файловые операции не понадобятся - он только в памяти хранится. Но это уже не для этого форума вопрос smile

Автор: Zloxa 11.11.2010, 17:27
Цитата(HeliosArt @  11.11.2010,  17:06 Найти цитируемый пост)
Только при этом логика разработчиков мне все равно не ясна: если уж AUTO-INC lock висит на таблице до завершения всего statement'а и в ходе запроса новые значения выделяются row-by-row, 

Ну, если еще пороешь, то может и нароешь, что для бульков лок отпускается  и, возможно, перезахватывается по мере нужды smile
Иначе, действительно, такая логика была бы странна.
Делалость то с целью оптимизации. А оптимизация зачастую требует в качестве жертвы пренебрежения фундаментальными правилами.

Вообще радует, что в документации о том чтото сказано. Хоть и прикопано основательно. Я был о масиной документации худшего мнения ;)

Автор: HeliosArt 11.11.2010, 17:48
Так в том-то и суть, что не отпускается.

Отпускают его только в interleaved(innodb_autoinc_lock_mode = 2), но там о разрывах и непоследовательности значений предупреждают сразу.

В остальных же режимах:

traditional (innodb_autoinc_lock_mode = 0):
    лок висит до конца при любой вставке

consecutive (innodb_autoinc_lock_mode = 1, стоит по умолчанию):
    при bulk-insert лок висит до конца, но для простых вставок вместо него используется mutex и таблица не блокируется вовсе

Автор: Zloxa 11.11.2010, 17:54
Проверить есть возможность?
Пустить какуюнить бульку минут на десять, и, тем временем, попытаться вставиться другой сессией?

Добавлено через 2 минуты и 1 секунду
правда надо еще как то убедиться что на той самой блокировке стоим..... а то мало ли на каком еще локе постоять доведется....

Автор: HeliosArt 11.11.2010, 18:32
Проверил в режиме consecutive: запустил 2 параллельных bulk-insert'а, первым более тяжелый, вторым попроще. В результате второй выполнился только после того, как был полностью закончен первый.

Причем в это время SHOW ENGINE INNODB STATUS по второму запросу показывал следующее:

Цитата

TABLE LOCK table `mydb`.`test_new` trx id F56C lock mode AUTO-INC waiting


То есть лок висит до конца, как и описано в документации

Автор: Zloxa 11.11.2010, 21:17
Премило.
Спасибо

Добавлено через 2 минуты и 6 секунд
я бы тот что попроще пускал не булком... чтоб совсем просто... ну да ладно. Это очень интересный результат. Еще раз спасибо.

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)