▶️Итак, ответ. Пока у вас транзакции следуют строго одна за другой - у вас всё хорошо и нет проблем. Кроме производительности. А чтобы улучшить производительность — необходимо разрешить транзакции выполнять параллельно. И вот тут начинается богатый и поразительный мир
concurrently control. При том, не только в БД, а везде где хоть что-то выполняется параллельно.Допустим есть пользователь, у него есть 100 денег на счету. Пользователь может их тратить, вы проверяете баланс
SELECT balance ..., затем обновляете баланс при покупке UPDATE ... SET balance = ? WHERE .... И вот в счастливый день как-то так вышло, что приходят сразу 2 запроса на покупки для этого пользователя. Одна на 50 денег, вторая на 70. Одна из них должна быть отклонена, т.к. денег недостаточно. Но в результате получается что обе покупки прошли и у вас проблема, вы продали то, что не надо было. И это даже не видно по балансу пользователя. Как же?Это типичный
race condition, обе транзакции сначала данные читают, потом локально что-то делают, потом что-то пишут.⏩читать им никто не мешает, потому обе транзакции прочитали что у пользователя 100 денег
⏩обе транзакции закономерно решили что денег достаточно
⏩обе транзакции обновили баланс пользователя
При конкурентном доступе к ресурсу подрались только на последнем шаге, транзакция которая начала обновлять данные позже сначала подождала завершение первой транзакции. А затем банально перезаписала баланс на тот который считала правильным сама —
lost update аномалия.▶️Вот как раз для того чтобы предупредить СУБД о том, что мы планируем с данными что-то делать, а потому нам надо сериализовать транзакции иначе, и существует
FOR SHARE, FOR UPDATE дополнения. Потому они кстати и задокументированы в разделе Explicit Locking📎 Подробнее прочитать можно тут
@sqlhub