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

Ответ

Блокировки — это фундаментальный механизм в транзакционных базах данных (таких как 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):

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

Ответ 18+ 🔞

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

1. Что конкретно запираем нахуй? (Уровень гранулярности)

Тут всё просто, как три копейки. Можно заблокировать:

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

2. А как именно запираем? (Режим доступа)

Тут два основных фокуса:

  • Разделяемая блокировка (Shared Lock, S-lock): Для чтения. Представь, что ты в библиотеке. Книгу (строку) могут одновременно читать несколько человек, но вырывать из неё страницы или писать в ней хуйню — нельзя. Пока хоть один читает, писателю — жопа.

    • На SQL: SELECT ... FOR SHARE (или в MySQL LOCK IN SHARE MODE).
  • Эксклюзивная блокировка (Exclusive Lock, X-lock): Для записи, обновления, удаления. Это когда ты заваливаешься в сортир, щёлкнув защёлкой. Только ты один и можешь там наводить красоту. Никто другой ни зайти, ни даже в замочную скважину посмотреть не сможет, пока ты не вылезешь.

    • На SQL: SELECT ... FOR UPDATE. Классика для UPDATE и DELETE.

3. Стратегия: параноик или пофигист?

А вот это уже философия, блядь. Как ты подходишь к жизни.

  • Пессимистичная блокировка (Pessimistic Locking): Стратегия параноика. «Все вокруг мудаки, сейчас нахуйарят». Поэтому ты сначала хватаешь ресурс в ежовые рукавицы, а потом с ним работаешь.

    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. Читаем запись. Видим: balance=1000, version=5.
    SELECT balance, version FROM accounts WHERE id = 1;
    
    -- 2. В приложении решаем снять 100...
    
    -- 3. Пытаемся обновить, но ТОЛЬКО если версия та же!
    UPDATE accounts
    SET balance = 900, version = 6 -- Версию увеличиваем
    WHERE id = 1 AND version = 5; -- Вот это — магическое условие

    Если после этого UPDATE вернул 0 строк — всё, пиздец, кто-то успел раньше. Данные устарели, надо начинать сначала. Волнение ебать, но зато в обычных ситуациях — летает.

  • Рекомендательные блокировки (Advisory Locks): А это вообще отдельная песня. Это как взять и повесить свой собственный амбарный замок на что угодно, хоть на дерево во дворе. Не привязаны к таблицам, просто синхронизируют логику приложения. Хочешь, чтобы два скрипта-демона не наступили друг другу на хвост — используй advisory lock. Удобная хуйня, когда нужно координировать что-то поверх БД.