Москва: проверенные б/у ноутбуки с гарантией и проверкой перед оплатой
+7 906 722-94-45

Главная → Статьи → Мультитенантность: модели изоляции данных

Мультитенантность: общая схема, схема на клиента и база на клиента

Мультитенантность: три модели изоляции данных, сравнение стоимости и рисков, row-level security в PostgreSQL и выбор гибридного подхода.

Прямой ответ

Мультитенантность — обслуживание нескольких клиентов (тенантов) одним экземпляром приложения. Ключевое решение — модель изоляции данных, и она определяет стоимость, безопасность и сложность сопровождения на годы вперёд.

Три базовые модели:

Модель Изоляция Стоимость на клиента Сложность миграций
Общая схема (pooled) Логическая, по tenant_id Минимальная Одна миграция на всех
Схема на клиента Физическая в пределах базы Средняя Миграция применяется к каждой схеме
База на клиента Полная Максимальная Отдельное применение к каждой базе

Общая схема — таблицы одни, каждая строка содержит идентификатор клиента:

CREATE TABLE orders (
    id          bigint GENERATED ALWAYS AS IDENTITY,
    tenant_id   uuid   NOT NULL,
    customer_id uuid   NOT NULL,
    total       numeric(12,2) NOT NULL,
    PRIMARY KEY (id)
);
CREATE INDEX ON orders (tenant_id, customer_id);

Плюсы: минимальная стоимость, одна миграция, простое масштабирование. Минусы: ошибка в фильтре приводит к утечке данных между клиентами, а крупный клиент создаёт нагрузку на всех.

Схема на клиента — отдельная схема в одной базе:

CREATE SCHEMA tenant_42;
CREATE TABLE tenant_42.orders (...);

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

База на клиента — отдельный экземпляр или база. Плюсы: полная изоляция, выполнение требований регулятора, независимое резервное копирование. Минусы: стоимость растёт линейно, миграции выполняются сотни раз, мониторинг усложняется.

Дополнительный уровень защиты в общей схеме — row-level security в PostgreSQL:

ALTER TABLE orders ENABLE ROW LEVEL SECURITY;

CREATE POLICY tenant_isolation ON orders
    USING (tenant_id = current_setting('app.tenant_id')::uuid);

Политика применяется на уровне базы, поэтому даже запрос без фильтра в коде не вернёт чужие строки. Это защита от ошибки разработчика, а не только от злоумышленника.

Сопровождение выбранной модели

Выбор модели определяет характер эксплуатации на годы, поэтому сопутствующие механизмы проектируются вместе с ней.

Миграции. При общей схеме миграция применяется один раз и завершается за секунды. При схеме на клиента та же миграция применяется сотни раз, и прерывание на середине оставляет часть клиентов обновлёнными, а часть — нет. Требуется журнал применения, возможность продолжить после сбоя и порядок, при котором отказ одного клиента не блокирует остальных.

Мониторинг. Метрики должны быть срезаны по клиентам, иначе аномальная нагрузка одного тенанта выглядит как общая деградация. Минимальный набор: число запросов, задержка, объём данных и число ошибок на клиента.

Резервное копирование. При общей схеме копия одна, но восстановление данных отдельного клиента требует выборочного извлечения. При базе на клиента копии независимы, а их число растёт вместе с числом клиентов.

Подключение к базе. Схема на клиента требует переключения схемы поиска для каждого соединения, что расходует пул. Общая схема этого не требует, но нуждается в установке контекста для row-level security на каждое соединение.

Удаление клиента. При общей схеме удаление данных клиента — запрос по всем таблицам с фильтром по идентификатору, требующий аккуратности и проверки полноты. При отдельной базе достаточно удалить базу.

Масштабирование. Общая схема масштабируется увеличением мощности одного экземпляра. При росте сверх возможностей экземпляра требуется шардирование по клиентам, что существенно сложнее первоначальной модели.

Практика: выбор модели и её сопровождение

1. Начинайте с общей схемы. Для большинства продуктов она достаточна, а переход к более строгой изоляции возможен позже для отдельных клиентов. Обратный переход — слияние баз — значительно сложнее.

2. Применяйте гибридную модель. Мелкие клиенты в общей схеме, крупные и регулируемые — в отдельных базах. Это стандартная практика, а не компромисс.

3. Закладывайте tenant_id с первого дня. Добавление колонки позже требует заполнения для существующих строк и изменения всех запросов. Стоимость отложенного решения растёт экспоненциально.

4. Включайте row-level security. Защита на уровне базы не зависит от корректности кода; о подходах к безопасности — в статье про безопасность баз данных.

5. Планируйте миграции под число тенантов. Применение миграции к 800 схемам занимает часы и может прерваться на середине. Нужен порядок, контроль ошибок и возможность продолжить; о секционировании — в статье про партиционирование.

6. Изолируйте шумных соседей. Один клиент с аномальной нагрузкой не должен замедлять остальных. Ограничение на уровне запросов и отдельная очередь для тяжёлых задач решают задачу частично.

7. Проверяйте фильтрацию тестами. Тест, который запрашивает данные под одним тенантом и ожидает пустой результат, обязателен; о рисках — в статье про фильтрацию по tenant_id.

Рекомендации:

  1. Фиксируйте модель в архитектурном решении. Смена модели изоляции — один из самых дорогих проектов в жизни продукта.
  2. Считайте стоимость на клиента. Разница между моделями становится заметной после сотни клиентов, когда менять что-либо поздно.
  3. Учитывайте требования к данным. Если клиент требует хранения в определённой юрисдикции, нужна отдельная база в соответствующем регионе.
  4. Проектируйте выгрузку и удаление данных клиента. При общей схеме удаление по требованию требует аккуратного запроса по всем таблицам.
  5. Измеряйте нагрузку по тенантам. Без метрик на клиента невозможно обнаружить шумного соседа до жалоб остальных.

Практический сценарий: переход к гибридной модели после роста до 400 клиентов

Контекст: продукт начинался с общей схемы. К 400 клиентам три крупных создавали более половины нагрузки, и задержка у остальных выросла.

  1. Диагностика. Метрики по тенантам показали, что три клиента выполняли тяжёлые отчётные запросы, нагружавшие общий экземпляр. Индексы помогали частично, поскольку запросы читали миллионы строк.
  2. Оценка моделей. Полный переход на базу на клиента означал 400 отдельных экземпляров и неприемлемую стоимость. Сохранение общей схемы — продолжение деградации.
  3. Действия. (а) Трёх крупных клиентов перевели в отдельные базы с сохранением той же схемы таблиц; (б) маршрутизацию подключения сделали по идентификатору тенанта на уровне фабрики соединений; (в) для остальных включили row-level security, поскольку до этого фильтрация была только в коде; (г) миграции автоматизировали с порядком применения и журналом, чтобы прерывание не требовало ручного разбора; (д) добавили метрику нагрузки по тенантам и порог, при превышении которого клиента переводят на выделенную базу.
  4. Результат. Задержка у мелких клиентов вернулась к прежнему уровню; стоимость выросла на три экземпляра вместо четырёхсот, а процедура перевода клиента стала типовой операцией.

Вывод: модель изоляции выбирается не раз и навсегда, а развивается вместе с распределением нагрузки. Гибрид требует маршрутизации подключения и автоматизации миграций, но обходится существенно дешевле полной изоляции.

FAQ

Можно ли начать с общей схемы и перейти к отдельным базам позже? Да, и это типичный путь. Сложность заключается в переносе данных и в маршрутизации подключения, а не в изменении схемы таблиц.

Достаточно ли фильтрации в коде без row-level security? Технически да, но одна пропущенная проверка приводит к утечке. Политика на уровне базы защищает от ошибки разработчика, поэтому её стоит включать дополнительно.

Как обслуживать сотни схем при миграциях? Автоматизировать применение с журналом и возможностью продолжить после сбоя. Ручное применение к сотням схем невыполнимо и не проверяемо.

Что делать с резервным копированием при разных моделях? При общей схеме достаточно одной копии. При базе на клиента нужны отдельные копии либо логическое разделение внутри общей копии; о стратегиях — в статье про резервное копирование.

Источники: PostgreSQL docs (row security), AWS SaaS isolation, Azure Architecture Center — ссылки в шапке.