Я слышал о проблемах параллелизма в MySQL раньше. Не так в Postgres.
Достаточно встроенных блокировок на уровне строк в уровне READ COMMITTEDизоляции транзакций по умолчанию .
Я предлагаю один оператор с CTE, изменяющим данные (чего у MySQL также нет), потому что удобно передавать значения из одной таблицы в другую напрямую (если вам это нужно). Если вам ничего не нужно из couponтаблицы, вы также можете использовать транзакцию с отдельным оператором UPDATEи INSERTоператорами.
WITH upd AS (
UPDATE coupon
SET used = true
WHERE coupon_id = 123
AND NOT used
RETURNING coupon_id, other_column
)
INSERT INTO log (coupon_id, other_column)
SELECT coupon_id, other_column FROM upd;
Редко случается , что более чем одна транзакция пытается выкупить один и тот же купон. У них есть уникальный номер, не так ли? Более чем одна попытка транзакции в один и тот же момент времени должна быть намного реже. (Может быть, ошибка приложения или кто-то пытается воспроизвести систему?)
Как бы то ни было, UPDATEединственное, что успешно выполняется только для одной транзакции, несмотря ни на что. Объект UPDATEполучает блокировку на уровне строк в каждой целевой строке перед обновлением. Если параллельная транзакция пытается получить доступ к UPDATEтой же строке, она увидит блокировку в строке и будет ждать завершения ( ROLLBACKили COMMIT) блокирующей транзакции , а затем станет первой в очереди блокировки:
Если совершено, перепроверьте условие. Если это все еще NOT used, заблокируйте строку и продолжайте. Иначе UPDATEтеперь не находит подходящей строки и ничего не делает, не возвращая строки, поэтому INSERTтакже ничего не делает.
В случае отката заблокируйте строку и продолжайте.
Там нет никакого потенциала для состояния гонки .
Там нет никакого потенциала для тупиковой ситуации , если вы положили больше операций записи в одной транзакции или иным образом заблокировать больше строк , чем просто один.
Это INSERTбеззаботно. Если по какой-то ошибке coupon_idуже есть в logтаблице (и у вас есть ограничение UNIQUE или PK log.coupon_id), вся транзакция будет откатана после уникального нарушения. Укажет недопустимое состояние в вашей БД. Если приведенное выше утверждение является единственным способом записи в logтаблицу, это никогда не должно происходить.