Gymterview
middle

Оптимистичная и пессимистичная блокировки

Блокировки решают проблему потерянных обновлений (lost updates), когда два потока одновременно читают и обновляют одну и ту же запись.

Оптимистичная блокировка (@Version)

Предполагает, что конфликты редки. Проверяет версию записи при обновлении. Если версия изменилась — бросает исключение.

Пример
@Entity
public class Product {
    @Id
    private Long id;
    private String name;
    private BigDecimal price;

    @Version  // Hibernate автоматически управляет этим полем
    private Long version;
}

Сгенерированный SQL:

Пример
-- При UPDATE Hibernate проверяет version:
UPDATE products SET name = ?, price = ?, version = 2
WHERE id = 1 AND version = 1;
-- Если version изменилась → 0 строк обновлено → OptimisticLockException

Пессимистичная блокировка (LockModeType)

Блокирует строку в БД (SELECT … FOR UPDATE), предотвращая конкурентный доступ.

Пример
// JPA
Product product = entityManager.find(Product.class, id,
    LockModeType.PESSIMISTIC_WRITE);
// SQL: SELECT * FROM products WHERE id = 1 FOR UPDATE

// Spring Data JPA
@Lock(LockModeType.PESSIMISTIC_WRITE)
@Query("SELECT p FROM Product p WHERE p.id = :id")
Optional<Product> findByIdForUpdate(@Param("id") Long id);

Типы LockModeType

Тип SQL Назначение
PESSIMISTIC_READ FOR SHARE Разрешает concurrent reads, блокирует writes
PESSIMISTIC_WRITE FOR UPDATE Блокирует и reads, и writes
PESSIMISTIC_FORCE_INCREMENT FOR UPDATE + version++ Для update с инкрементом версии

Сравнение

Критерий Оптимистичная Пессимистичная
Механизм Проверка версии при UPDATE Блокировка строки в БД
Конфликт Обнаружение (exception) Предотвращение (ожидание)
Производительность Выше при редких конфликтах Выше при частых конфликтах
Deadlock Нет Возможен

Важное

  • Оптимистичная — @Version, проверка при UPDATE, исключение при конфликте; подходит для большинства сценариев
  • Пессимистичная — FOR UPDATE, блокировка строки; для критичных операций (списание средств, резервирование)
  • @Version может быть Long, Integer, Timestamp (Long предпочтительнее)
  • Оптимистичная блокировка работает и между HTTP-запросами (stateless)

Частые ошибки

  • Не использовать блокировки вообще — lost updates в конкурентном приложении неизбежны
  • Пессимистичная блокировка без таймаута — бесконечное ожидание при deadlock; задайте javax.persistence.lock.timeout
  • @Version на поле, которое обновляется вручную — Hibernate должен управлять version автоматически
  • Оптимистичная блокировка для частых конфликтов — постоянные OptimisticLockException ухудшают UX; используйте пессимистичную

Как используется в 2026

  • Оптимистичная блокировка (@Version) — стандарт по умолчанию для большинства сущностей
  • Пессимистичная — для финансовых операций, инвентаря, резервирования
  • Для распределённых систем — distributed locks (Redis, Zookeeper) вместо БД-блокировок

На собеседовании: начните с проблемы lost updates, затем покажите два решения. Оптимистичная — @Version, подходит в 90% случаев. Пессимистичная — FOR UPDATE, для финансов и критичных операций. Интервьюер может спросить про deadlock при пессимистичной блокировке — упомяните таймаут.