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

Ответ

Блокировки — это фундаментальный механизм в транзакционных базах данных (таких как PostgreSQL, MySQL), который обеспечивает целостность данных (изоляцию транзакций). Их можно классифицировать по нескольким критериям.

1. По уровню гранулярности (что блокируем):

  • Блокировка таблицы (Table Lock): Блокируется вся таблица. Самый низкий уровень параллелизма, но и самые низкие накладные расходы на управление блокировками.
  • Блокировка страницы (Page Lock): Блокируется страница данных (блок на диске, содержащий несколько строк).
  • Блокировка строки (Row Lock): Блокируется одна или несколько строк. Обеспечивает наивысший уровень параллелизма, но требует больше ресурсов для управления.

2. По режиму доступа (как блокируем):

  • Разделяемая (Shared Lock, S-lock):

    • Назначение: Для чтения данных.
    • Правило: Несколько транзакций могут одновременно удерживать S-lock на одном и том же ресурсе. Это позволяет им параллельно читать данные.
    • Конфликт: Блокирует установку эксклюзивной блокировки (X-lock). Пока есть хоть одна S-lock, никто не может изменить данные.
    • SQL-пример (PostgreSQL/MySQL): SELECT ... FOR SHARE (или LOCK IN SHARE MODE в MySQL).
  • Эксклюзивная (Exclusive Lock, X-lock):

    • Назначение: Для изменения или удаления данных (UPDATE, DELETE).
    • Правило: Только одна транзакция может удерживать X-lock на ресурсе.
    • Конфликт: Блокирует установку любых других блокировок (и S-lock, и X-lock). Никто не может ни читать, ни изменять данные, пока X-lock не будет снята.
    • SQL-пример (PostgreSQL/MySQL): SELECT ... FOR UPDATE.

3. По стратегии получения блокировки:

Это не типы блокировок как таковые, а подходы к их использованию на уровне приложения.

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

    • Идея: «Предполагаем, что конфликт вероятен». Блокировка ресурса происходит до выполнения операции, чтобы предотвратить конфликты.
    • Реализация: Используются явные S-lock или X-lock (FOR UPDATE/FOR SHARE). Транзакция захватывает ресурс и держит его до своего завершения (COMMIT/ROLLBACK).
    • Пример:
      BEGIN;
      -- Блокируем строку от изменений другими транзакциями
      SELECT * FROM accounts WHERE id = 1 FOR UPDATE;
      -- Безопасно обновляем баланс
      UPDATE accounts SET balance = balance - 100 WHERE id = 1;
      COMMIT;
  • Оптимистичная блокировка (Optimistic Locking):

    • Идея: «Предполагаем, что конфликты маловероятны». Блокировка не используется. Вместо этого перед обновлением проверяется, не изменились ли данные с момента их чтения.
    • Реализация: Обычно используется поле version или updated_at в таблице.
    • **Пример (логика на уровне приложения):

      -- 1. Читаем запись вместе с ее версией
      SELECT balance, version FROM accounts WHERE id = 1; 
      -- (Приложение получает balance=1000, version=5)
      
      -- 2. Приложение выполняет логику...
      
      -- 3. Пытаемся обновить, проверяя, что версия не изменилась
      UPDATE accounts 
      SET balance = 900, version = 6 -- Увеличиваем версию
      WHERE id = 1 AND version = 5; -- Условие на старую версию

      Если UPDATE затронул 0 строк, значит, другая транзакция уже изменила данные. В этом случае операцию нужно повторить (прочитать данные заново и попробовать снова).

  • Рекомендательные блокировки (Advisory Locks):

    • Описание: Это блокировки, управляемые приложением, которые не привязаны к таблицам или строкам. Они представляют собой просто блокировку по строковому или числовому ключу. Полезны для синхронизации фоновых задач или выполнения операций, которые не связаны с конкретной строкой в БД.