Coarse Grained Lock (Грубая блокировка)
Паттерн проектирования Coarse Grained Lock
Описание Coarse Grained Lock
Блокирует набор связанных объектов при помощи одной блокировки.
Объекты часто могут отредактироваться в группе. Например, есть клиент и набор его адресов. В таком случае можно блокировать их все, если надо обратиться к одному из них. Блокировка их по отдельности приведёт к множеству проблем. Во-первых все кто пытается работать с такими данными, будет вынужден создавать код для поиска всех записей, чтобы потом заблокировать их. Это достаточно просто в примере с клиентом и адресами, но что, если группа будет более сложной или групп будет больше. Куда поместить часть программной логики, которая отвечает за слияние блокировок. Если в стратегию блокировки входит правило, в соответствии с которым объект должен быть загружен перед блокировкой, как например в Optimistic Offline Lock , то блокировка большого массива объектов вызовет провал в производительности. А в случае с Pessimistic Offline Lock , большая блокировка вызовет проблемы с управлением и увеличит конкуренцию за блокировки.
Паттерн Coarse-Grained Lock представляет собой одиночную блокировку, которая покрывает множество объектов. Она не только упрощает работу с блокировками, но ещё и освобождает разработчика от необходимости загружать все элементы группы, чтобы заблокировать их.
Реализация в популярных СУБД
PostgreSQL
PostgreSQL поддерживает грубую блокировку через различные механизмы:
- Table-Level Locks - блокировки на уровне таблиц
- Schema-Level Locks - блокировки на уровне схем
- Database-Level Locks - блокировки на уровне базы данных
-- Блокировка таблицы для обновления
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 поддерживает грубую блокировку через различные уровни:
- Table Locks - блокировки таблиц
- Database Locks - блокировки баз данных
- Global Locks - глобальные блокировки
-- Блокировка таблицы
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 предоставляет мощные механизмы для грубой блокировки:
- Table Locks - блокировки таблиц
- Schema Locks - блокировки схем
- Database Locks - блокировки базы данных
-- Блокировка таблицы для обновления
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 поддерживает грубую блокировку через различные механизмы:
- Table Hints - подсказки для таблиц
- Schema Locks - блокировки схем
- Database Locks - блокировки базы данных
-- Блокировка таблицы
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 использует файловые блокировки для грубой блокировки:
- Database Locks - блокировки базы данных
- File Locks - файловые блокировки
- Shared Cache Locks - блокировки общего кэша
-- Блокировка базы данных
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 | Низкая | Очень ограниченная |
Рекомендации по использованию
- PostgreSQL - лучший выбор для веб-приложений с умеренной конкурентностью
- MySQL - подходит для простых приложений с низкой конкурентностью
- Oracle - для корпоративных приложений с высокими требованиями
- SQL Server - для Windows-среды и .NET приложений
- SQLite - только для встраиваемых приложений
Паттерны реализации
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
);
Использована иллюстрация с сайта Мартина Фаулера.