PGagliardi Ответов: 1

Как правильно заблокировать таблицу MYSQL в параллельной среде?


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

Итак, вот такая ситуация: например, давайте представим, что курс имеет 15 свободных мест, и я делаю свой заказ. По простой логике, когда я сделал свой заказ, система должна решить, нахожусь ли я в списке прямого бронирования (один из 15 доступных слотов) или в списке ожидания. Чтобы сделать это, мы должны подсчитать, если общее количество прямых резерваций меньше общего количества доступных резерваций, что-то вроде этого в псевдокоде:

INSERT new_partecipant IN table_partecipants;
(my_id) = SELECT @@IDENTITY;
(total_reservations_already_made) = SELECT COUNT(*) FROM table_partecipants WHERE flag_direct_reservation=1;
if total_reservations_already_made <= total_reservations_available then
UPDATE table_partecipants SET flag_direct_reservation=1 WHERE id=my_id


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

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

Что я уже пробовал:

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

Michael_Davies

Вы читали руководство MySQL по блокировке таблиц?

https://dev.mysql.com/doc/refman/5.7/en/lock-tables.html

PGagliardi

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

ZurdoDev

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

Вставить в таблицу 1 (field1, flag_direct_reservation)
SELECT @field1, CASE WHEN (SELECT COUNT (*) FROM sometable) > (SELECT COUNT(*) FROM othertable) ТОГДА 1 ИНАЧЕ 0 КОНЕЦ

PGagliardi

Звучит неплохо! Но можете ли вы привести рабочий пример также и для оператора UPDATE?

Попробовать это:

Обновление таблицы mytable настроить подписку = (случай, когда (выбрать количество(*) из таблицы mytable, где поле=идентификатор_курса) &ГТ; max_reservations тогда 0 иначе 1 конец) где ID=id_subscription

но я получил следующую ошибку:

"Вы не можете указать целевую таблицу "mytable" для обновления в предложении from".

ZurdoDev

Вам придется поискать его в MySql, извините.

1 Ответов

Рейтинг:
0

PGagliardi

Ладно, похоже, мы нашли решение::

ВСТАВЛЯТЬ

INSERT INTO table1 (field1, field2, ..., fieldN, flag_direct_reservation)
SELECT @field1, @field2, ..., @fieldN, ,CASE WHEN (SELECT COUNT(*) FROM sometable WHERE course=@course_id) > @max_part THEN 1 ELSE 0 END


ОБНОВЛЕНИЕ
(только для определения статуса подписки, в случае удаления подписки)


UPDATE corsi_mytable p1 INNER JOIN 
(
    SELECT COUNT(*) as actual_subscritions
    FROM mytable
    WHERE course=@course_id
)p2 
SET p1.flag_direct_reservation= CASE WHEN p2.actual_subscritions > @max_part THEN 0 ELSE 1 END 
WHERE p1.id =@first_waiting_id;