Содержание

Мультитенантность — это архитектура, в которой одно приложение обслуживает многих клиентов, а их данные изолированы друг от друга. На старте большинству SaaS-продуктов подходит общая база данных с идентификатором клиента в каждой таблице: она дешевле в поддержке, а крупных клиентов с особыми требованиями позже можно вынести в отдельные базы.

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

Какие бывают модели мультитенантности?

Есть три основные модели: все клиенты в общих таблицах, у каждого клиента своя схема в общей базе или у каждого своя база данных. Чем сильнее изоляция, тем выше стоимость инфраструктуры и сложнее обновления.

Модель Изоляция Стоимость Обновления схемы Когда выбирать
Общие таблицы с tenant_id Логическая Низкая Одна миграция Большинство SaaS на старте
Схема на клиента Средняя Средняя Миграция для каждой схемы Десятки–сотни клиентов, B2B
База на клиента Полная Высокая Миграция для каждой базы Требования регуляторов, крупные контракты

Как устроить общую базу безопасно?

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

// Глобальный скоуп: все запросы модели фильтруются по текущему клиенту
class TenantScope implements Scope
{
    public function apply(Builder $builder, Model $model): void
    {
        $builder->where($model->getTable() . '.tenant_id', Tenant::current()->id);
    }
}

Дополнительные уровни защиты:

  • политики безопасности на уровне строк в PostgreSQL как вторая линия обороны;
  • идентификатор клиента в составных индексах и уникальных ограничениях;
  • автотесты, которые проверяют, что клиент А не видит данные клиента Б;
  • отдельные префиксы для кеша, файлов и очередей каждого клиента.

Про типичные уязвимости веб-приложений — в статье OWASP Top 10 простыми словами.

Холл с лифтами и стойкой ресепшен Фото: Unsplash

Когда нужна отдельная база для клиента?

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

Цена изоляции:

  1. Миграции нужно прогонять по всем базам и следить, чтобы ни одна не отстала.
  2. Отчёты по всем клиентам собираются из многих источников.
  3. Резервные копии, мониторинг и подключения множатся с каждым клиентом.
  4. Новому клиенту нужно создать базу — это автоматизируют при регистрации.

Что выбрать на старте?

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

Что заложить сразу:

  • идентификатор клиента во всех таблицах с клиентскими данными, даже если сейчас кажется лишним;
  • определение текущего клиента в одном месте — по поддомену, домену или токену;
  • лимиты по тарифу, чтобы один клиент не забрал все ресурсы;
  • выгрузку данных клиента — она понадобится и для переноса, и по требованию закона.

О том, как связать архитектуру с тарифами, мы писали в статьях модель монетизации SaaS и юнит-экономика SaaS.

Как не допустить проблем с «шумными соседями»?

«Шумный сосед» — клиент, который своей нагрузкой замедляет остальных. Защищаются от этого лимитами и разделением ресурсов: тяжёлые операции идут через очереди с квотами на клиента, а API ограничивает частоту запросов.

  • ограничение частоты запросов к API по клиенту и тарифу;
  • отдельные очереди или приоритеты для импорта, экспорта и отчётов;
  • мониторинг нагрузки в разрезе клиентов, чтобы видеть, кто её создаёт;
  • переселение крупных клиентов на выделенные ресурсы без изменения кода.

Коротко

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

Частые вопросы

  • Можно ли перейти с общей базы на отдельные позже?

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

  • Как давать клиентам свои домены?

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

  • Что делать, если один клиент нагружает систему больше остальных?

    Ввести лимиты по тарифу, вынести тяжёлые операции в очереди с квотами и при необходимости переселить крупного клиента на выделенные ресурсы.