Задать вопрос
@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;



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



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

Но иногда про это забывают и делают не поле + 1, а полученное-ранее-значение-поля + 1. И тогда при конкурентном доступе счётчик начинает работать неправильно и нужно использовать блокировку (а лучше - нормальный запрос выше, который её не требует).
Ответ написан
tsklab
@tsklab
Здесь отвечаю на вопросы.
С картинками
Явная транзакция:
НАЧАТЬ
ИЗМЕНИТЬ баланс
ЕСЛИ баланс < 0
  ТО ОТКАТИТЬ
  ИНАЧЕ ЗАВЕРШИТЬ

Неявная транзакция (запускается для каждой операции INSERT, UPDATE или DELETE). Делаете для таблицы ОГРАНИЧЕНИЕ баланс >= 0. И в неявной транзакции ИЗМЕНИТЬ будет ОТКАТИТЬ при балансе < 0.
Ответ написан
@nApoBo3
В целом вы рассуждает верно.
Но, первое это не блокировка строк.
Второе при update на время транзакции тоже будут блокировки и их нужно понимать.
Конкретно ваш кейс проще решается через ограничение на значение ниже нуля и обработка исключения в коде.
Ответ написан
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы