Coarse Grained Lock (Грубая блокировка)

Паттерн проектирования Coarse Grained Lock

Паттерн проектирования Coarse Grained Lock

Описание Coarse Grained Lock

Блокирует набор связанных объектов при помощи одной блокировки.

Объекты часто могут отредактироваться в группе. Например, есть клиент и набор его адресов. В таком случае можно блокировать их все, если надо обратиться к одному из них. Блокировка их по отдельности приведёт к множеству проблем. Во-первых все кто пытается работать с такими данными, будет вынужден создавать код для поиска всех записей, чтобы потом заблокировать их. Это достаточно просто в примере с клиентом и адресами, но что, если группа будет более сложной или групп будет больше. Куда поместить часть программной логики, которая отвечает за слияние блокировок. Если в стратегию блокировки входит правило, в соответствии с которым объект должен быть загружен перед блокировкой, как например в Optimistic Offline Lock , то блокировка большого массива объектов вызовет провал в производительности. А в случае с Pessimistic Offline Lock , большая блокировка вызовет проблемы с управлением и увеличит конкуренцию за блокировки.

Паттерн Coarse-Grained Lock представляет собой одиночную блокировку, которая покрывает множество объектов. Она не только упрощает работу с блокировками, но ещё и освобождает разработчика от необходимости загружать все элементы группы, чтобы заблокировать их.

Реализация в популярных СУБД

PostgreSQL

PostgreSQL поддерживает грубую блокировку через различные механизмы:

-- Блокировка таблицы для обновления
BEGIN;
LOCK TABLE users IN EXCLUSIVE MODE;
-- Работа с данными...
UPDATE users SET status = 'active' WHERE department_id = 1;
UPDATE user_addresses SET is_primary = false WHERE user_id IN (
    SELECT id FROM users WHERE department_id = 1
);
COMMIT;

-- Блокировка с таймаутом
LOCK TABLE users IN EXCLUSIVE MODE NOWAIT;

MySQL (InnoDB)

MySQL поддерживает грубую блокировку через различные уровни:

-- Блокировка таблицы
LOCK TABLES users WRITE, user_addresses WRITE;
-- Работа с данными...
UPDATE users SET status = 'active' WHERE department_id = 1;
UPDATE user_addresses SET is_primary = false WHERE user_id IN (
    SELECT id FROM users WHERE department_id = 1
);
UNLOCK TABLES;

-- Блокировка с таймаутом
LOCK TABLES users WRITE, user_addresses WRITE;
-- Если блокировка не получена, операция завершится с ошибкой

Oracle Database

Oracle предоставляет мощные механизмы для грубой блокировки:

-- Блокировка таблицы для обновления
LOCK TABLE users IN EXCLUSIVE MODE;
-- Работа с данными...
UPDATE users SET status = 'active' WHERE department_id = 1;
UPDATE user_addresses SET is_primary = false WHERE user_id IN (
    SELECT id FROM users WHERE department_id = 1
);
COMMIT;

-- Блокировка с NOWAIT
LOCK TABLE users IN EXCLUSIVE MODE NOWAIT;

SQL Server

SQL Server поддерживает грубую блокировку через различные механизмы:

-- Блокировка таблицы
BEGIN TRANSACTION;
SELECT * FROM users WITH (TABLOCKX) WHERE department_id = 1;
-- Работа с данными...
UPDATE users SET status = 'active' WHERE department_id = 1;
UPDATE user_addresses SET is_primary = false WHERE user_id IN (
    SELECT id FROM users WHERE department_id = 1
);
COMMIT;

-- Блокировка с таймаутом
SELECT * FROM users WITH (TABLOCKX, READPAST) WHERE department_id = 1;

SQLite

SQLite использует файловые блокировки для грубой блокировки:

-- Блокировка базы данных
BEGIN EXCLUSIVE;
-- Работа с данными...
UPDATE users SET status = 'active' WHERE department_id = 1;
UPDATE user_addresses SET is_primary = false WHERE user_id IN (
    SELECT id FROM users WHERE department_id = 1
);
COMMIT;

-- Блокировка с таймаутом
BEGIN IMMEDIATE;
-- Если блокировка не получена, операция завершится с ошибкой

Сравнение производительности

СУБД Тип блокировок Производительность Масштабируемость
PostgreSQL Table-level + Row-level Высокая Хорошая
MySQL Table-level + Row-level Средняя Ограниченная
Oracle Table-level + Row-level Очень высокая Отличная
SQL Server Table-level + Row-level Высокая Хорошая
SQLite Database-level Низкая Очень ограниченная

Рекомендации по использованию

Паттерны реализации

Parent-Child Locking

-- Блокировка родительской записи
LOCK TABLE departments IN EXCLUSIVE MODE;
-- Работа с дочерними записями
UPDATE users SET status = 'active' WHERE department_id = 1;
UPDATE user_addresses SET is_primary = false WHERE user_id IN (
    SELECT id FROM users WHERE department_id = 1
);

Aggregate Root Locking

-- Блокировка корневого агрегата
LOCK TABLE orders IN EXCLUSIVE MODE;
-- Работа с составными частями
UPDATE orders SET status = 'shipped' WHERE id = 1;
UPDATE order_items SET shipped_quantity = quantity WHERE order_id = 1;
UPDATE order_addresses SET is_shipping = true WHERE order_id = 1;

Domain Locking

-- Блокировка домена
LOCK TABLE user_domains IN EXCLUSIVE MODE;
-- Работа с объектами домена
UPDATE users SET status = 'active' WHERE domain_id = 1;
UPDATE user_roles SET is_active = true WHERE user_id IN (
    SELECT id FROM users WHERE domain_id = 1
);

Использована иллюстрация с сайта Мартина Фаулера.

Источник