Что такое оптимистическая блокировка (optimistic locking) и как она реализуется?

Ответ

Оптимистическая блокировка — это стратегия управления конкурентным доступом к данным, которая предполагает, что конфликты между транзакциями происходят редко. Вместо явной блокировки записи на время транзакции используется механизм проверки версии.

Принцип работы:

  1. Каждая версионируемая сущность (например, строка в БД) имеет поле-версию (обычно числовое или timestamp).
  2. При чтении данных клиент также получает текущее значение версии.
  3. При обновлении в запрос добавляется условие, что версия не изменилась с момента чтения: UPDATE table SET data = 'new', version = version + 1 WHERE id = ? AND version = ?.
  4. Если количество обновленных строк равно 0 (версия изменилась другим транзакцией), фиксируется оптимистическое исключение (например, OptimisticLockException в JPA). Приложение должно обработать его (например, повторить операцию).

Реализация с JPA/Hibernate:

@Entity
public class Account {
    @Id
    private Long id;
    private BigDecimal balance;

    @Version // Аннотация для включения оптимистической блокировки
    private Long version; // Поле автоматически инкрементится при каждом UPDATE
}

// В сервисе. При конфликте будет выброшено OptimisticLockException
@Transactional
public void updateBalance(Long accountId, BigDecimal newBalance) {
    Account account = entityManager.find(Account.class, accountId);
    account.setBalance(newBalance);
    // При коммите Hibernate выполнит UPDATE с проверкой version
}
Преимущества и недостатки: Преимущества Недостатки
Высокая масштабируемость, нет долгих блокировок Требует обработки конфликтов на уровне приложения
Уменьшает вероятность взаимных блокировок (deadlocks) Неэффективна при очень высокой конкуренции за одни данные
Простая реализация через ORM

Ответ 18+ 🔞

А, оптимистическая блокировка! Ну это как в жизни, блядь. Представь: идешь ты в бар, видишь последний кусок пиццы на тарелке. Ты такой: «О, класс, сейчас сожру!». Но ты же не будешь стоять над ним с ножом, крича «Моё!», пока его жуёшь? Нет, ты запомнил, что он там лежит, пошёл за пивом, вернулся — а его уже, сука, сосед слопал. Вот это и есть оптимистический подход: надеешься, что никто не тронет, пока ты отлучился. А если тронули — ну, пиздец, придётся заказывать новую или драться.

Вот так и в базе. Мы не ставим тяжёлый замок на запись, а просто вешаем на неё бирочку — версию. Читаем данные, запоминаем, что версия, допустим, 5.

@Entity
public class Account {
    @Id
    private Long id;
    private BigDecimal balance;

    @Version // Вот эта волшебная хуйня, которая всё и делает
    private Long version;
}

Потом хотим обновить. Говорим базе: «Слушай, дай-ка я обновлю этот счёт, но только если его версия до сих пор 5, а не какая-нибудь ебаная 6».

UPDATE account SET balance = 1000, version = 6 WHERE id = 123 AND version = 5;

И если в этот момент другой чувак уже успел проапдейтить запись и версия стала 6, то твой запрос нихуя не обновит — ноль строк затронуто. ORM это видит и такой: «Опа, накрылось всё медным тазом!» — и швыряет тебе в рожу OptimisticLockException. Твоё приложение должно не охуеть, а обработать это: либо вывести пользователю «Извините, данные устарели, обновите страницу», либо, если ты не ленивая жопа, автоматически повторить операцию с новыми данными.

Плюсы и минусы, если по-простому:

Плюсы:

  • Масштабируемость — овердохуищная. Нет этих долбанных блокировок, которые всё тормозят. Транзакции не висят, как сопли, ожидая друг друга.
  • Deadlock'ов меньше — потому что никто никого не держит за жопу, все просто надеются на лучшее.
  • Реализация — проще пареной репы. Воткнул @Version и вперёд.

Минусы:

  • Конфликты на твоей совести — если два юзера одновременно лезут в одну запись, одному придётся получить по ебалу исключением. Надо это обрабатывать, а не просто игнорить, как многие делают.
  • При высокой конкуренции — пиздец. Если над одной записью двадцать человек колдуют каждую секунду, то оптимистическая блокировка превращается в игру «успей первым». Все будут постоянно получать исключения и перезапускать операции. Тут уже надо думать про пессимистические блокировки или менять архитектуру.

В общем, инструмент отличный, но не серебряная пуля. Как говорится, доверия ебать ноль, всегда проверяй версию.