Ответ
Мультитенантность — это архитектурный принцип, позволяющий одному экземпляру приложения обслуживать несколько изолированных групп пользователей (тенантов). Я реализовывал его, делая выбор между тремя основными моделями, исходя из требований к изоляции, сложности и стоимости.
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 как опцию.