Я слышал о проблемах параллелизма в 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
таблицу, это никогда не должно происходить.