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

«Как решали проблемы мультитенантности в распределенных системах?» — вопрос из категории Распределенные системы, который задают на 33% собеседований Data Инженер. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

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

1. Раздельные базы данных (Database per Tenant)

  • Что это: Каждый тенант получает свою физически отдельную БД (или схему в терминах некоторых СУБД).
  • Когда использовать: Для максимальной изоляции и безопасности данных, когда требования тенантов сильно различаются или есть строгие нормативные ограничения (например, GDPR).
  • Пример из практики: В SaaS-платформе для финтеха мы использовали эту модель для клиентов высшего ценового сегмента. Подключение к БД определялось по субдомену (client1.app.com -> db_client1). Управление миграциями усложнялось, но изоляция была полной.

2. Общая схема, раздельные таблицы (Shared Database, Separate Schemas)

  • Что это: Все тенанты используют одну БД, но у каждого свой набор таблиц в отдельной схеме.
  • Когда использовать: Хороший баланс между изоляцией и простотой управления. Легко делать резервные копии или удалять данные одного тенанта.
  • Пример на SQL:
    -- Создание изолированного пространства для тенанта
    CREATE SCHEMA tenant_acme;
    SET search_path TO tenant_acme;
    CREATE TABLE orders (id SERIAL, amount DECIMAL); -- Таблица создается в схеме tenant_acme

3. Общая схема, общие таблицы (Shared Database, Shared Schema)

  • Что это: Все данные всех тенантов хранятся в одних и тех же таблицах с колонкой tenant_id.
  • Когда использовать: Для максимальной масштабируемости и эффективности использования ресурсов, когда все тенанты имеют одинаковую структуру данных.
  • Ключевая задача — гарантировать изоляцию:
    • На уровне приложения: Все запросы должны включать фильтр WHERE tenant_id = ?. Мы использовали паттерн "шлюза запросов" (Query Interceptor) в ORM (например, в Hibernate или Django), который автоматически добавлял этот фильтр.
    • На уровне БД: Использование Row Level Security (RLS) в PostgreSQL, чтобы даже прямой доступ к БД не позволял увидеть чужие данные.
      ALTER TABLE orders ENABLE ROW LEVEL SECURITY;
      CREATE POLICY tenant_isolation_policy ON orders
          USING (tenant_id = current_setting('app.current_tenant_id')::INT);

Выбор модели всегда был компромиссом. Для нового продукта мы начинали с модели 3 (самой простой), а для клиентов с особыми требованиями предлагали модель 1 или 2 как опцию.