Как работает пессимистичная блокировка на уровне базы данных
Что проверяют
Знаете ли вы синтаксис и понимаете ли, когда она уместнее оптимистичной.
Как отвечать
Пессимистичная блокировка захватывает строку в момент чтения, и другие транзакции, которым нужна та же строка, ждут до конца вашей транзакции.
begin;
select balance from accounts where id = 1 for update;
-- никто другой не изменит эту строку до commit
update accounts set balance = balance - 100 where id = 1;
commit;Варианты: FOR UPDATE даёт исключительную блокировку, FOR SHARE разделяемую (читать можно, изменять нельзя).
Две очень полезные опции, которые стоит назвать:
NOWAIT: вместо ожидания сразу возвращается ошибка. Годится, когда лучше быстро сказать пользователю «занято», чем держать его в ожидании;SKIP LOCKED: занятые строки просто пропускаются. Это стандартный способ построить очередь задач прямо в базе: каждый обработчик берёт свободные строки и не конкурирует с остальными.
В JPA то же самое выражается режимами блокировки, и Hibernate добавляет нужный суффикс к запросу.
Когда выбирать пессимистичную:
- конфликты частые: несколько процессов регулярно спорят за одну строку (остаток товара, распределение уникального ресурса);
- повтор дорог: пересчитывать всю операцию заново хуже, чем подождать.
Когда лучше оптимистичная (версия строки, конфликт обнаруживается при записи): конфликты редки, а транзакция включает время раздумий пользователя. Держать блокировку, пока человек заполняет форму, недопустимо.
Риски пессимистичной блокировки: снижение параллелизма, длинные ожидания, а главное взаимные блокировки, когда две транзакции берут те же строки в разном порядке. Лечится единым порядком захвата и коротким временем удержания. Внутри такой транзакции нельзя делать сетевые вызовы.
Где спотыкаются
Блокируют строку и ждут действия пользователя.
Не задают порядок захвата и получают взаимные блокировки.
Не знают про пропуск занятых строк и городят очередь на стороне приложения.
Считают, что обычное чтение блокирует строку.