В чем разница между оптимистичной (Optimistic) и пессимистичной (Pessimistic) блокировкой в базах данных?

Ответ

Оптимистичная блокировка предполагает, что конфликты при одновременном изменении данных маловероятны. Она проверяет, не изменились ли данные с момента их чтения, только в момент фиксации транзакции (commit). Если данные были изменены другой транзакцией, выбрасывается исключение (например, OptimisticLockException). Реализуется через поля версии (@Version) или метки времени (timestamp).

Пессимистичная блокировка предполагает частые конфликты и предотвращает их, блокируя строку или таблицу на уровне базы данных сразу при чтении для изменения. Это гарантирует эксклюзивный доступ, но снижает параллелизм.

Ключевые различия:

  • Подход: Оптимистичная — «проверить и обновить», пессимистичная — «заблокировать, затем изменить».
  • Блокировка в БД: Оптимистичная не устанавливает блокировок при чтении, пессимистичная — устанавливает (например, SELECT ... FOR UPDATE).
  • Производительность: Оптимистичная обычно обеспечивает лучшую производительность при низкой конкуренции. Пессимистичная может приводить к ожиданиям и deadlock'ам.
  • Использование: Оптимистичная подходит для сценариев с редкими конфликтами (веб-приложения). Пессимистичная — для критичных данных с высокой конкуренцией (финансовые операции).

Пример оптимистичной блокировки (JPA/Hibernate):

@Entity
public class Account {
    @Id
    private Long id;
    private BigDecimal balance;
    @Version
    private Long version; // Автоматически увеличивается при каждом обновлении
}
// При коммите, если version в БД отличается от version в объекте, будет выброшено OptimisticLockException

Пример пессимистичной блокировки (JPA):

// Блокировка строки для эксклюзивного изменения
Account account = entityManager.find(Account.class, accountId, LockModeType.PESSIMISTIC_WRITE);
// Или с использованием запроса
Query query = entityManager.createQuery("SELECT a FROM Account a WHERE a.id = :id");
query.setParameter("id", accountId);
query.setLockMode(LockModeType.PESSIMISTIC_WRITE);
Account account = (Account) query.getSingleResult();

Ответ 18+ 🔞

А, слушай, вот эта вся хуйня с блокировками в базах данных! Ну, типа, есть два главных подхода, как два брата-близнеца, только один — оптимист, а второй — законченный пессимист и параноик.

Оптимистичная блокировка — это как наивный студент перед сессией: «Да ладно, шпоры никто не заметит, пронесёт!». Она работает по принципу «прочитал — изменил — попытался сохранить». В момент сохранения (коммита) она вдруг вспоминает и проверяет: «А не изменил ли кто-то эти данные, пока я тут со своими мыслями сидел?». Если изменил — тебе в лоб летит исключение, типа OptimisticLockException, и всё, иди переделывай. Вся её магия крутится вокруг какого-нибудь поля версии (@Version) или временной метки.

Пессимистичная блокировка — это уже злой охранник в стрёмном клубе: «Ты чё пришёл? Стоять! Не двигаться! Руки на стол!». Она с самого начала, как только ты посмотрел на данные, чтобы их поменять, хватает их и вешает на них здоровенный замок на уровне самой базы данных. Типа SELECT ... FOR UPDATE. Никто другой тронуть эту строку не сможет, пока ты не закончишь. Надёжно? Да. Но если таких охранников много, они друг другу начинают мозги выносить, образуются очереди и deadlock'и, ёпта.

Так в чём разница, спросишь ты?

  • Философия: Оптимист — «авось пронесёт», пессимист — «все вокруг враги, надо блокировать».
  • Блокировка в БД: Оптимист не парится, читает свободно. Пессимист сразу хватает и не отпускает.
  • Скорость: Если народу мало и все друг другу не мешают — оптимист летает. Пессимист всегда тормозит из-за своей паранойи.
  • Где юзать: Оптимист — для всяких веб-морд, где данные редко друг другу мешают. Пессимист — для расчёта зарплат или переводов денег, где ошибка стоит дорого.

Вот, смотри, как оптимист выглядит в коде (JPA/Hibernate):

@Entity
public class Account {
    @Id
    private Long id;
    private BigDecimal balance;
    @Version
    private Long version; // Вот эта хитрая жопа! При каждом апдейте сама увеличивается.
}
// Пытаешься закоммитить, а Hibernate смотрит: версия в базе и у тебя в объекте совпадают? Если нет — получай исключение в ебало!

А вот пессимист, мрачный тип (тоже JPA):

// Подходишь к базе с таким лицом и говоришь: "Эта запись — моя, блядь!"
Account account = entityManager.find(Account.class, accountId, LockModeType.PESSIMISTIC_WRITE);
// Или через запрос, чтоб уж наверняка
Query query = entityManager.createQuery("SELECT a FROM Account a WHERE a.id = :id");
query.setParameter("id", accountId);
query.setLockMode(LockModeType.PESSIMISTIC_WRITE); // Заблокировал, и всем остальным — низкий поклон!
Account account = (Account) query.getSingleResult();

Короче, выбирай: либо летаешь, но рискуешь получить конфликт и переделывать работу, либо ползешь, зато уверен, что никто тебе не помешает. Как в жизни, блядь.