Задать вопрос
@X110111

Правильно ли я понимаю работу с блокировками строк?

Добрый день.
Если я правильно всё понимаю, блокировки нам нужны для обеспечения корректного поведения при конкурентном доступе.
Типичный пример - работа с балансом пользователя.
Если услуга стоит 20, а у пользователя на балансе 30, то теоретически возможна ситуация, когда две конкурирующие транзакции считывают значение 30, приложение видит что всё ок и дважды предоставляет услугу, хотя денег хватает только на одно списание.
Именно для этого и нужно использовать блокировку.

  • Начинаем транзакцию.


  • Производим блокировку.
    SELECT balance FROM users WHERE id = 1 LIMIT 1 FOR UPDATE;



  • В приложении проверяем что денег достаточно и производим списание.
    UPDATE users SET balance = balance - 20 WHERE id = 1;



  • Фиксируем транзакцию.


Верно?

А вот если нам не нужно делать никаких предварительных проверок, то и блокировка нам тоже не нужна?
Например, если мы делаем какой-нибудь счётчик просмотров. В этом случае нам достаточно будет выполнить
UPDATE articles SET views = views + 1 WHERE id = 1;

И в этом случае всё и так будет корректно работать и никаких "потерянных" значений у нас не будет?

Если последнее утверждение верно, то в случае перевода с баланса одного пользователя на баланс другого пользователя, мы должны заблокировать того, с кого списываем, но того, кому зачисляем, блокировать не нужно?
Грубо говоря, мы должны получить примерно следующее?

  • Старт транзакции.


  • Чтение с блокировкой.
    SELECT balance FROM users WHERE id = 1 LIMIT 1 FOR UPDATE;



  • Проверяем что баланс достаточен.


  • Производим списание.
    UPDATE users SET balance = balance - 50 WHERE id = 1;



  • Производим зачисление.
    UPDATE users SET balance = balance + 50 WHERE id = 2;



  • Фиксируем транзакцию.



Верно ли я всё понимаю, или какие есть ошибки?
  • Вопрос задан
  • 82 просмотра
Подписаться 1 Простой Комментировать
Решение пользователя Алексей Уколов К ответам на вопрос (3)
Да, в целом и по сути всё верно. Но иногда бывают нюансы. Например, вот такой запрос правильный и блокировки не требует:
UPDATE articles SET views = views + 1 WHERE id = 1;

Но иногда про это забывают и делают не поле + 1, а полученное-ранее-значение-поля + 1. И тогда при конкурентном доступе счётчик начинает работать неправильно и нужно использовать блокировку (а лучше - нормальный запрос выше, который её не требует).
Ответ написан