Многопоточное добавление в БД mysql

99
20 апреля 2021, 12:10

Есть приложение C#, есть база данных mysql, используется Dapper. Приложение в несколько потоков добавляет записи в БД таким способом:

id = connection.Query<int>("INSERT INTO messages блаблабла; SELECT LAST_INSERT_ID();", message).FirstOrDefault();

Правильно ли я понимаю, что, учитывая многопоточность, SELECT LAST_INSERT_ID() в данном контексте вероятно может выдавать неправильные значения? Или же mysql как-то это учитывает?

Answer 1

Как сказано в документации:

When a new AUTO_INCREMENT value has been generated, you can also obtain it by executing a SELECT LAST_INSERT_ID() statement with mysql_query() and retrieving the value from the result set returned by the statement.

When inserting multiple values, the last automatically incremented value is returned.

For LAST_INSERT_ID(), the most recently generated ID is maintained in the server on a per-connection basis. It is not changed by another client. It is not even changed if you update another AUTO_INCREMENT column with a nonmagic value (that is, a value that is not NULL and not 0). Using LAST_INSERT_ID() and AUTO_INCREMENT columns simultaneously from multiple clients is perfectly valid. Each client will receive the last inserted ID for the last statement that client executed.

Таким образом,

Для LAST_INSERT_ID () последний сгенерированный идентификатор сохраняется на сервере для каждого соединения. Это не влияет на других клиентов. [...] Использование столбцов LAST_INSERT_ID () и AUTO_INCREMENT одновременно от нескольких клиентов вполне допустимо. Каждый клиент получит последний вставленный идентификатор для последнего оператора, выполненного клиентом.

Поскольку для MySQL понятия "клиент" и "соединение" эквивалентны в данном контексте, а Ваше приложение "в несколько потоков добавляет записи в БД", то если каждый поток имеет свое собственное соединение с БД - он для MySQL равнозначен отдельному клиенту. И тогда его запросы на LAST_INSERT_ID() никак не влияют на соседние потоки. Если же (прошу пардону, в C# не шарю) эти несколько потоков пользуют одно соединение с базой, как-то по другому решая проблему доступности общего ресурса, то SQL-запрос стоит завернуть в транзакцию, как рекомендует @Андрей NOP:

START TRANSACTION;
INSERT INTO messages блаблабла;
SELECT LAST_INSERT_ID();
COMMIT;

Хотя... Вызов Вашей пары запросов "вставка - чтение" идут в рамках одного обращения к методу connection.Query() и должны отправиться на сервер как один запрос. Соответственно, пока не будет получен на него ответ - общий ресурс "соединение к БД" освободиться не должен бы. И, соответственно, другой поток не получит к нему доступа. Должно бы и так проканать. Но каши маслом не испортишь, лучше все же завернуть в транзакцию. Главное - чтобы при этом у таблицы не был движок MyISAM. :)

READ ALSO
Ошибка Invalid Reverse Solidus &#39;\&#39; declaration при разборе json

Ошибка Invalid Reverse Solidus '\' declaration при разборе json

Прошу помощи в разборе JSON

283
Entity FrameWork защита от SQL инъекций

Entity FrameWork защита от SQL инъекций

Начал изучать Entity FrameWork подNet MVC, реализую DB First-то бишь есть готовая база Mysql и с помощью EF создаю сущности DBContext и т

107
Как определить формат файла excel?

Как определить формат файла excel?

Если не вдаваться в подробности, попадаются файлыxlsx которые названы как

138
Конвертирование string в int c#

Конвертирование string в int c#

Есть стринговое значение:

112