Ответ
Блокировки — это фундаментальный механизм в транзакционных базах данных (таких как 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):
- Описание: Это блокировки, управляемые приложением, которые не привязаны к таблицам или строкам. Они представляют собой просто блокировку по строковому или числовому ключу. Полезны для синхронизации фоновых задач или выполнения операций, которые не связаны с конкретной строкой в БД.