Какие основные стратегии блокировок используются при работе с базой данных?

Ответ

Блокировки обеспечивают согласованность данных в многопользовательской среде. Существует две фундаментальные стратегии.

1. Оптимистичная блокировка (Optimistic Locking)

Предпосылка: Конфликты при одновременном изменении данных редки. Проверка конфликта происходит в момент коммита транзакции. Реализация: Через поле версии или временной метки.

  • JPA/Hibernate пример:

    @Entity
    public class Account {
        @Id
        private Long id;
        private BigDecimal balance;
    
        @Version // Специальное поле для оптимистичной блокировки
        private Long version;
    }

    Механизм: При чтении объекта фиксируется его version. При обновлении в запросе UPDATE добавляется условие WHERE id=? AND version=?. Если версия изменилась (другой поток уже обновил запись), количество обновлённых строк будет 0, и JPA выбросит OptimisticLockException. Плюсы: Высокая производительность, нет долгих блокировок. Минусы: Частые конфликты приводят к большому числу откатов.

2. Пессимистичная блокировка (Pessimistic Locking)

Предпосылка: Конфликты вероятны. Данные блокируются на время транзакции. Реализация в JPA:

// Блокировка при чтении (обычно SELECT ... FOR UPDATE)
Account account = entityManager.find(
    Account.class, 
    accountId, 
    LockModeType.PESSIMISTIC_WRITE
);

// Или явная блокировка позже
entityManager.lock(account, LockModeType.PESSIMISTIC_WRITE);

Типы пессимистичных блокировок в JPA:

  • PESSIMISTIC_READ: Разделяемая блокировка (shared lock). Другие транзакции могут читать, но не изменять данные.
  • PESSIMISTIC_WRITE: Эксклюзивная блокировка (exclusive lock, FOR UPDATE). Другие транзакции не могут ни читать, ни изменять данные (зависит от СУБД).
  • PESSIMISTIC_FORCE_INCREMENT: Блокировка с одновременным увеличением поля @Version.

Плюсы: Гарантирует целостность, предотвращает конфликты. Минусы: Риск взаимоблокировок (deadlock) и снижение производительности из-за ожиданий.

Уровни блокировок в СУБД

  • Блокировка строк (Row-level lock): Блокируется только изменяемая строка. Наиболее гранулярный и предпочтительный уровень.
  • Блокировка страниц (Page-level lock): Блокируется страница данных (группа строк).
  • Блокировка таблиц (Table-level lock): Блокируется вся таблица. Сильно снижает параллелизм, используется для DDL-операций или массовых обновлений.

Выбор стратегии: Используйте оптимистичную блокировку для сценариев с высокой конкуренцией на чтение и низкой — на запись. Пессимистичную — для критичных данных, где конфликт при одновременном изменении недопустим (например, финансовые операции).

Ответ 18+ 🔞

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

1. Оптимистичная блокировка (Optimistic Locking)

Суть её в чём: Мы делаем вид, что все вокруг — адекватные люди и редко когда лезут в одну и ту же запись одновременно. То есть, работаем спокойно, а проверяем, не обосрались ли мы, только в самый последний момент, когда сохраняем. Как это выглядит технически: Заводим специальное поле — версию или timestamp.

  • Вот как в Hibernate это, сука, делается:

    @Entity
    public class Account {
        @Id
        private Long id;
        private BigDecimal balance;
    
        @Version // Вот эта вот волшебная хуйня!
        private Long version;
    }

    Как работает этот фокус: Читаешь ты запись, запоминаешь её version. Потом, когда пишешь её обратно, в апдейте добавляется хитрая проверка: WHERE id=? AND version=?. И если за это время какой-то другой мудак уже успел её обновить и версию увеличить — твой апдейт нихуя не сработает (ноль строк обновится), и тебе выкинут OptimisticLockException. Всё, иди нахуй, начинай заново. Плюсы: Быстро, блядь, не вешаем замки на всё подряд. Минусы: Если конфликты реально частые, то ты будешь только и делать, что ловить эти ебучие исключения и переделывать работу. Пиздец, а не жизнь.

2. Пессимистичная блокировка (Pessimistic Locking)

А вот это уже серьёзно: Здесь мы исходим из того, что все вокруг — ебучие хамы и обязательно полезут в наши данные. Поэтому мы сразу, как только посмотрели на запись, кричим: "Моё! Не трогай, сука!" и вешаем на неё замок. Делается это в JPA так:

// Берём запись и сразу же блокируем её на запись (это типа SELECT ... FOR UPDATE)
Account account = entityManager.find(
    Account.class, 
    accountId, 
    LockModeType.PESSIMISTIC_WRITE
);

// Или можно потом припереться и залочить
entityManager.lock(account, LockModeType.PESSIMISTIC_WRITE);

Какие бывают замки:

  • PESSIMISTIC_READ: Замок "только посмотреть". Другие могут тоже читать, но хуй кто что изменит.
  • PESSIMISTIC_WRITE: Полный, блядь, эксклюзив. FOR UPDATE в чистом виде. Ни читать, ни писать, пока я не отпущу. В общем, иди нахуй.
  • PESSIMISTIC_FORCE_INCREMENT: Комбо-удар: и замок повесил, и версию увеличил, на всякий случай.

Плюсы: Полная, блядь, гарантия, что пока ты работаешь, никто тебе в тарелку не плюнет. Минусы: Риск дедлоков (когда два мудака повисли друг на друге и ждут), и производительность может просесть, потому что все стоят в очереди.

А ещё замки бывают разного калибра, ёпта:

  • На строку (Row-level lock): Идеально. Блокируем только ту строчку, которую трогаем. Все довольны.
  • На страницу (Page-level lock): Уже хуже. Блокируется кусок данных, где несколько строк. Может зря кого-то зацепить.
  • На всю таблицу (Table-level lock): Ну это пиздец полный. Вешаем амбарный замок на всю таблицу. Используется, когда надо что-то глобально поменять, а в обычной жизни — терпения ебать ноль, если так делать.

Так что же выбрать, спросишь ты? Если у тебя народ в основном читает, а пишет редко — оптимистичная, и не еби мозг. Если же у тебя, например, финансовый перевод и два человека пытаются списать деньги с одного счета — тут без вариантов, пессимистичная, и пусть все ждут своей очереди, как в советской поликлинике.