MySQL атомная вставка-если-не-существует со стабильным автоинкремент

В MySQL я использую таблицу InnoDB, которая содержит уникальные имена и идентификаторы для этих имен. Клиентам необходимо атомизировать проверку существующего имени, вставить новый, если он не существует, и получить идентификатор. Идентификатор - это значение AUTO_INCREMENT, и он не должен увеличивать контроль над отсутствием контроля при проверке существующих значений независимо от настройки "innodb_autoinc_lock_mode" ; это потому, что очень часто одно и то же имя будет проверяться (например, "Alice" ), и время от времени будет появляться новое имя (например, "Bob" ).

Оператор "INSERT...ON DUPLICATE KEY UPDATE" вызывает увеличение AUTO_INCREMENT даже в случае с дубликат-ключ, в зависимости от "innodb_autoinc_lock_mode", и поэтому неприемлемо. Идентификатор будет использоваться как цель ограничения внешних ключей (в другой таблице), и, следовательно, изменить существующие идентификаторы нехорошо. Клиенты не должны заходить в тупик, когда они выполняют это действие одновременно, независимо от того, как операции могут чередоваться.

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

Моя тестовая таблица, чтобы продемонстрировать это, называется FirstNames:

CREATE TABLE `FirstNames` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `FirstName` varchar(45) COLLATE utf8mb4_unicode_ci NOT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `FirstName_UNIQUE` (`FirstName`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

Самое лучшее решение, которое я придумал до сих пор, выглядит следующим образом:

COMMIT;
SET @myName='Alice';
SET @curId=NULL;
SET autocommit=0;
LOCK TABLES FirstNames WRITE;
SELECT Id INTO @curId FROM FirstNames WHERE FirstName = @myName;
INSERT INTO `FirstNames` (`FirstName`) SELECT @myName FROM DUAL WHERE @curId IS NULL;
COMMIT;
UNLOCK TABLES;
SET @curId=IF(@curId IS NULL, LAST_INSERT_ID(), @curId);
SELECT @curId;

Это использует "LOCK TABLES...WRITE", следуя инструкциям, приведенным в документации по MySQL "Взаимодействие таблицы и транзакций" для правильного пути для блокировки таблиц InnoDB. Это решение требует, чтобы пользователь имел привилегию "LOCK TABLES".

Если я запустил указанный выше запрос с помощью @myName="Alice", я получу новый идентификатор, а затем продолжу получать тот же ID независимо от того, сколько раз я его запускаю. Если я запускаю с @myName="Bob", я получаю другой идентификатор со следующим значением AUTO_INCREMENT и так далее. Проверка имени, которое уже существует, не увеличивает значение таблицы AUTO_INCREMENT.

Мне интересно, есть ли лучшее решение для этого, возможно, такое, которое не требует команд "LOCK TABLES" / "UNLOCK TABLES" и объединяет более "рудиментарные" команды (например, "INSERT" и "SELECT" ) более умным способом? Или это лучшая методология, которую MySQL может предложить в настоящее время?

Изменить

Это не дубликат " "Как вставить, если не существует" в MySQL?". Этот вопрос не касается всех критериев, которые я изложил. Вопрос о сохранении стабильного значения AUTO_INCREMENT не разрешен там (это упоминается только попутно).

Многие ответы не касаются получения идентификатора существующей/вставленной записи, некоторые из ответов не обеспечивают атомную операцию, а некоторые ответы имеют логику, выполняемую на стороне клиента, а не на сервере -боковая сторона. Ряд ответов изменяет существующую запись, и это не то, что я ищу. Я прошу либо лучший способ выполнить все указанные критерии, либо подтвердить, что мое решение является оптимальным с существующей поддержкой MySQL.

Ответ 1

Вопрос в том, как нормализовать данные, когда вы ожидаете дублирования. А затем избегайте "горящих" идентификаторов.

http://mysql.rjweb.org/doc.php/staging_table#normalization обсуждает двухэтапный процесс и нацелен на массовые обновления из-за высокоскоростного проглатывания строк. Он вырождается в одну строку, но все еще требует выполнения двух шагов.

Шаг 1 INSERTs любые новые строки, создающие новые идентификаторы auto_inc.

Шаг 2 оттягивает иды в массе.

Обратите внимание, что работу лучше всего выполнять с помощью autocommit = ON и вне основной транзакции, которая загружает данные. Это позволяет избежать дополнительной причины для сжигания идентификаторов, а именно потенциальных откатов.