Чем оптимистическая блокировка отличается от пессимистической и когда какую выбрать?
- Как работает @Version в JPA?
- Что такое SELECT FOR UPDATE?
- Как не допустить, чтобы два пользователя затёрли правки друг друга?
Пессимистическая — сначала блокируем строку (SELECT ... FOR UPDATE), потом меняем; остальные ждут. Оптимистическая — не блокируем, а при записи проверяем версию: UPDATE ... WHERE version = ?; 0 строк — кто-то успел раньше, повторяем или сообщаем.
- Пессимистическая — при частых конфликтах и коротких транзакциях
- Оптимистическая — при редких конфликтах и долгом «думании» между чтением и записью (форма редактирования)
- Оптимистическая работает и между HTTP-запросами (ETag, If-Match), пессимистическая — только внутри транзакции
А ещё Это тот же CAS, только в базе
Подробно
Пессимистическая блокировка исходит из того, что конфликт вероятен:
BEGIN;
SELECT * FROM seats WHERE id = 42 FOR UPDATE; -- другие FOR UPDATE ждут
UPDATE seats SET booked_by = 7 WHERE id = 42;
COMMIT;
Плюсы: просто, конфликт разрешается ожиданием, а не ошибкой. Минусы: блокировки держатся до конца транзакции — долгая транзакция тормозит остальных; возможны дедлоки при захвате нескольких строк в разном порядке; нельзя держать блокировку, пока пользователь смотрит на форму. Варианты: NOWAIT (сразу ошибка), SKIP LOCKED (взять следующую свободную — очереди задач в таблице).
Оптимистическая блокировка считает конфликт редким. У строки есть колонка version (или updated_at):
- читаем строку вместе с версией;
- меняем данные в приложении — сколько угодно долго;
- записываем с проверкой:
UPDATE ... SET ..., version = version + 1 WHERE id = ? AND version = ?; - обновлена 0 строк — кто-то изменил раньше: перечитать и повторить или показать пользователю конфликт.
В JPA/Hibernate это поле с @Version и исключение OptimisticLockException. В HTTP — ETag в ответе и If-Match в запросе на изменение: сервер вернёт 412 Precondition Failed, если ресурс изменился.
Как выбирать:
- конфликты редки, чтений много — оптимистическая: нет ожиданий и дедлоков;
- конфликты часты (горячая строка — остаток товара на распродаже) — оптимистическая будет постоянно откатываться; лучше пессимистическая или атомарный
UPDATE stock SET qty = qty - 1 WHERE id = ? AND qty > 0; - между запросами пользователя (редактирование документа, корзина) — только оптимистическая;
- распределённые системы: условная запись в DynamoDB,
WATCHв Redis, версия ключа в etcd — та же оптимистическая идея.
Часто лучший вариант — вообще не читать перед записью: атомарное изменение одним запросом не требует ни той, ни другой блокировки.
Пример
-- оптимистическая: два администратора правят одну карточку товара
SELECT id, title, price, version FROM products WHERE id = 5; -- version = 12
UPDATE products SET price = 990, version = 13
WHERE id = 5 AND version = 12; -- 1 строка — успех; 0 строк — конфликтЧто хотят услышать
- Механика обеих: FOR UPDATE и проверка версии.
- Критерий выбора: частота конфликтов и длина «окна».
- Оптимистическая между HTTP-запросами (ETag/If-Match, @Version).
- Атомарный UPDATE как альтернатива для счётчиков — плюс.
Частые ошибки
- Думать, что оптимистическая блокировка что-то блокирует в базе.
- Держать FOR UPDATE, пока ждём ответа внешнего сервиса или пользователя.
Проверьте себя
Один верный вариант. Подумайте, потом откройте ответ.
Показать варианты и ответ
- ✗Оптимистическая использует SELECT ... FOR UPDATE и выгодна при частых конфликтах; пессимистическая проверяет номер версии при записи и подходит для долгого редактирования
- ✗Пессимистическая лучше при редких конфликтах и долгом редактировании
- ✓Пессимистическая блокирует строку заранее; оптимистическая проверяет версию при записи (UPDATE ... WHERE version = ?) и выгодна при редких конфликтах
- ✗При оптимистической блокировке конфликт невозможен
Связанные вопросы
Разбор собеседования с sotto
После собеседования sotto разбирает разговор: оценка, каждый вопрос — хорошо, частично или слабо, чего не хватило в ответе и над чем поработать к следующему разу. Первые 30 минут бесплатно.