Multi-tenant vs single-tenant | Wybór właściwej architektury SaaS
Techniczne porównanie architektur multi-tenant i single-tenant w SaaS. Poznaj kompromisy i dowiedz się, jak wybrać właściwe podejście dla swojego produktu.
Każdy produkt SaaS musi odpowiedzieć na fundamentalne pytanie: jak obsługiwać wielu klientów z tego samego oprogramowania? Odpowiedź to Twój model tenancy. Wpływa on na koszty, bezpieczeństwo, wydajność i szybkość wdrażania. Właściwy wybór na początku chroni przed bolesnymi migracjami później.
Definicje
Multi-tenant
Jedna instancja aplikacji obsługuje wszystkich klientów. Wszyscy dzielą te same serwery, tę samą bazę kodu i często tę samą bazę danych. Dane każdego klienta są logicznie izolowane, ale infrastruktura jest współdzielona.
Pomyśl o tym jak o bloku mieszkalnym. Każdy najemca mieszka w tym samym budynku, dzieli windę i korytarze, ale ma własne zamknięte mieszkanie z własnym wyposażeniem.
Single-tenant
Każdy klient dostaje własną dedykowaną instancję aplikacji. Oddzielne serwery, oddzielne bazy danych, czasem oddzielne bazy kodu. Nic nie jest współdzielone między klientami.
Pomyśl o tym jak o wolnostojących domach. Każdy najemca ma własny budynek, własną hydraulikę, własny ogródek. Pełna izolacja, ale droższe w utrzymaniu.
Jak działa multi-tenancy
Istnieją trzy powszechne podejścia do architektury multi-tenant, każde z innymi kompromisami.
Model 1: Współdzielona aplikacja, współdzielona baza danych
Wszyscy tenanci dzielą jedną instancję aplikacji i jedną bazę danych. Dane tenantów są rozdzielane za pomocą identyfikatora tenanta (zazwyczaj kolumna tenant_id) w każdej tabeli.
Architektura:
- Jeden serwer aplikacji (lub klaster) obsługuje wszystkie żądania.
- Jedna baza danych przechowuje dane wszystkich tenantów.
- Każde zapytanie zawiera klauzulę
WHERE tenant_id = ?w celu ograniczenia danych do właściwego tenanta.
Zalety:
- Najniższy koszt infrastruktury. Jedna baza danych, jedno wdrożenie aplikacji.
- Najprostsze wdrażanie i aktualizacja. Wdróż raz, wszyscy otrzymują zmianę.
- Łatwa agregacja danych między tenantami do analityki i raportowania.
Wady:
- Najwyższe ryzyko wycieku danych, jeśli zapytanie zapomni o filtrze tenanta.
- Jeden hałaśliwy tenant może pogorszyć wydajność dla wszystkich.
- Migracje bazy danych dotyczą wszystkich tenantów jednocześnie.
- Najtrudniejsza zgodność z regulacjami dotyczącymi rezydencji danych.
To najpowszechniejsze podejście dla produktów B2B SaaS z dużą liczbą małych i średnich klientów.
Model 2: Współdzielona aplikacja, oddzielne bazy danych
Wszyscy tenanci dzielą tę samą instancję aplikacji, ale każdy tenant dostaje własną bazę danych. Aplikacja kieruje zapytania do właściwej bazy na podstawie uwierzytelnionego tenanta.
Architektura:
- Jeden serwer aplikacji (lub klaster) obsługuje wszystkie żądania.
- Oddzielna baza danych dla każdego tenanta.
- Warstwa routingu mapuje tożsamość tenanta na właściwe połączenie z bazą.
Zalety:
- Silniejsza izolacja danych. Brak ryzyka zapytań cross-tenant.
- Łatwiejsze spełnienie wymagań rezydencji danych. Możesz umieścić każdą bazę w wymaganym regionie tenanta.
- Backup i przywracanie per-tenant jest proste.
- Duże obciążenie jednego tenanta nie zablokuje tabel dla innych.
Wady:
- Więcej infrastruktury do zarządzania. Setki tenantów to setki baz danych.
- Migracje schematu muszą być stosowane do każdej bazy indywidualnie.
- Raportowanie cross-tenant wymaga odpytywania wielu baz.
- Wyższy koszt niż model w pełni współdzielony.
To dobry środek dla produktów wymagających lepszej izolacji bez kosztu pełnej single-tenancy.
Model 3: Oddzielna aplikacja, oddzielna baza danych
Każdy tenant dostaje własną instancję aplikacji i własną bazę danych. W pełni izolowane. To zasadniczo single-tenancy, ale zarządzane przez dostawcę SaaS zamiast klienta.
Architektura:
- Dedykowane wdrożenie aplikacji per tenant.
- Dedykowana baza danych per tenant.
- Warstwa routingu (często load balancer lub API gateway) kieruje ruch do właściwej instancji.
Zalety:
- Pełna izolacja. Brak współdzielonych zasobów między tenantami.
- Maksymalna elastyczność personalizacji per tenant.
- Wydajność jednego tenanta nigdy nie wpływa na innego.
- Najprostsze uzasadnienie compliance.
Wady:
- Zdecydowanie najwyższy koszt infrastruktury.
- Złożoność wdrożeń rośnie liniowo z liczbą tenantów.
- Aktualizacje muszą być wdrażane do każdej instancji indywidualnie (lub bardzo starannie zautomatyzowane).
- Nie skaluje się ekonomicznie dla dużej liczby małych tenantów.
Ten model ma sens dla enterprise SaaS, gdzie klienci wymagają dedykowanej infrastruktury i są gotowi za to zapłacić premium.
Zalety i wady w pigułce
| Czynnik | Multi-tenant (współdzielona DB) | Multi-tenant (oddzielne DB) | Single-tenant |
|---|---|---|---|
| Koszt infrastruktury | Najniższy | Średni | Najwyższy |
| Izolacja danych | Logiczna (row-level) | Fizyczna (database-level) | Pełna |
| Złożoność wdrożeń | Niska | Średnia | Wysoka |
| Szybkość aktualizacji | Natychmiastowa dla wszystkich | Natychmiastowa aplikacja, migracje per-DB | Wdrożenie per-instancja |
| Personalizacja | Ograniczona | Ograniczona | Pełna |
| Compliance | Trudniejszy | Umiarkowany | Najłatwiejszy |
| Skalowalność (liczba tenantów) | Doskonała | Dobra | Słaba |
| Ryzyko hałaśliwego sąsiada | Wysokie | Średnie | Brak |
Izolacja danych i bezpieczeństwo
Izolacja danych to najważniejsze rozważanie w każdym modelu tenancy. Naruszenie bezpieczeństwa, w którym jeden tenant może uzyskać dostęp do danych innego tenanta, jest katastrofalne dla biznesu SaaS. Może zniszczyć zaufanie, naruszyć regulacje i zakończyć działalność firmy.
Row-level security
W modelu współdzielonej bazy danych Row-Level Security (RLS) PostgreSQL to jedna z najsilniejszych obrona przed wyciekiem danych. RLS wymusza izolację tenantów na poziomie bazy danych, nie aplikacji. Nawet jeśli Twój kod aplikacji ma błąd, który zapomina filtrować po tenancie, sama baza zapobiega dostępowi cross-tenant.
Oto jak to skonfigurować:
-- Włącz RLS na tabeli
ALTER TABLE projects ENABLE ROW LEVEL SECURITY;
-- Stwórz politykę ograniczającą dostęp do bieżącego tenanta
CREATE POLICY tenant_isolation ON projects
USING (tenant_id = current_setting('app.current_tenant_id')::uuid);
-- Wymuś RLS nawet dla właścicieli tabeli
ALTER TABLE projects FORCE ROW LEVEL SECURITY;
Przed wykonaniem jakiegokolwiek zapytania ustaw kontekst tenanta:
-- Ustaw na początku każdego żądania/transakcji
SET LOCAL app.current_tenant_id = 'a1b2c3d4-e5f6-7890-abcd-ef1234567890';
-- Teraz to zapytanie automatycznie zwraca tylko dane bieżącego tenanta
SELECT * FROM projects;
-- Bez klauzuli WHERE. RLS się tym zajmuje.
Izolacja na poziomie aplikacji
Oprócz bezpieczeństwa na poziomie bazy danych, Twoja aplikacja powinna wymuszać granice tenantów:
// Middleware ustawiający kontekst tenanta na każdym żądaniu
async function tenantMiddleware(req: Request, res: Response, next: NextFunction) {
const tenantId = extractTenantId(req); // Z JWT, subdomeny lub nagłówka
if (!tenantId) {
return res.status(401).json({ error: "Tenant not identified" });
}
// Ustaw kontekst tenanta dla połączenia z bazą
await db.raw(`SET LOCAL app.current_tenant_id = '${tenantId}'`);
req.tenantId = tenantId;
next();
}
Obrona w głąb to zasada. Nigdy nie polegaj na jednej warstwie izolacji. Łącz filtrowanie na poziomie aplikacji, bezpieczeństwo na poziomie bazy danych i regularne audyty bezpieczeństwa.
Rozważania wydajnościowe
Wyzwania współdzielonej bazy danych
W współdzielonej bazie danych wszyscy tenanci konkurują o te same zasoby. Tenant uruchamiający ciężki raport może spowolnić zapytania dla wszystkich. Sposoby łagodzenia:
- Connection pooling. Użyj PgBouncer lub podobnego narzędzia, by zapobiec wyczerpaniu połączeń przez jednego tenanta.
- Timeouty zapytań. Ustaw maksymalne czasy wykonania, by niekontrolowane zapytanie nie blokowało zasobów w nieskończoność.
- Rate limiting. Egzekwuj per-tenant limity na żądania API i operacje bazodanowe.
- Read repliki. Kieruj ciężkie operacje odczytu (raporty, eksporty) na replikę, by nie wpływały na główną bazę.
Zalety oddzielnych baz danych
Z per-tenant bazami danych izolacja wydajności jest wbudowana. Ciężkie obciążenie jednego tenanta wpływa tylko na jego własną bazę. Możesz też provisionować większe lub mniejsze bazy w zależności od planu i użycia każdego tenanta.
Kompromisem jest narzut zarządzania. Monitorowanie 500 baz jest trudniejsze niż monitorowanie jednej. Automatyzacja staje się niezbędna.
Implikacje kosztowe na skali
Przyjrzyjmy się przybliżonym liczbom. Załóżmy, że masz 100 tenantów.
Współdzielona baza danych (multi-tenant):
- 1 klaster aplikacji: ~200 EUR/miesiąc
- 1 zarządzana instancja PostgreSQL: ~100 EUR/miesiąc
- Łącznie: ~300 EUR/miesiąc (3 EUR na tenanta)
Oddzielne bazy danych (multi-tenant):
- 1 klaster aplikacji: ~200 EUR/miesiąc
- 100 małych instancji baz danych: ~2000 EUR/miesiąc
- Łącznie: ~2200 EUR/miesiąc (22 EUR na tenanta)
Single-tenant:
- 100 instancji aplikacji: ~5000 EUR/miesiąc
- 100 instancji baz danych: ~2000 EUR/miesiąc
- Łącznie: ~7000 EUR/miesiąc (70 EUR na tenanta)
Te liczby są uproszczone, ale wzorzec się utrzymuje. Współdzielona infrastruktura jest dramatycznie tańsza. Przy 1000 tenantach różnica staje się jeszcze większa.
Dlatego cennik musi być zgodny z architekturą. Jeśli pobierasz 20 EUR miesięcznie i prowadzisz infrastrukturę single-tenant za 70 EUR na tenanta, tracisz pieniądze na każdym kliencie.
Kiedy wybrać multi-tenant
Architektura multi-tenant to właściwy wybór, gdy:
- Budujesz SaaS B2B dla SMB. Duży wolumen, niższe ceny i standardowe zestawy funkcji.
- Efektywność kosztowa ma znaczenie. Musisz utrzymać koszty infrastruktury niskie w stosunku do przychodów.
- Chcesz szybkich, jednolitych aktualizacji. Wdróż raz, wszyscy tenanci otrzymują ulepszenie.
- Twoi klienci nie wymagają dedykowanej infrastruktury. Większość małych i średnich firm nie przejmuje się, gdzie ich dane się znajdują, o ile są bezpieczne.
- Jesteś na wczesnym etapie. Zacznij od multi-tenant. Jest tańsze i prostsze. Zawsze możesz zaoferować single-tenant później dla klientów enterprise.
Kiedy wybrać single-tenant
Architektura single-tenant ma sens, gdy:
- Sprzedajesz do enterprise. Duże organizacje często wymagają dedykowanej infrastruktury jako części procesu zakupowego.
- Compliance tego wymaga. Branże takie jak ochrona zdrowia (HIPAA), finanse (SOX, PCI-DSS) i administracja mają surowe wymagania izolacji danych.
- Klienci potrzebują personalizacji. Jeśli każdy klient wymaga innych konfiguracji, integracji lub nawet funkcji, single-tenant daje Ci elastyczność.
- Twój cennik to wspiera. Jeśli pobierasz 5000 EUR lub więcej miesięcznie na klienta, koszt infrastruktury jest łatwo uzasadniony.
- Rezydencja danych jest twardym wymaganiem. Gdy dane muszą znajdować się w konkretnym kraju, oddzielna infrastruktura per tenant to najprostsze podejście.
Podejścia hybrydowe
Nie musisz wybierać tylko jednego. Wiele udanych firm SaaS stosuje model hybrydowy:
- Multi-tenant dla standardowych taryf. Darmowy, starter i pro klienci dzielą infrastrukturę. To utrzymuje koszty niskie i pozwala skalować.
- Single-tenant dla enterprise. Klienci premium dostają dedykowane instancje z niestandardowymi SLA, certyfikacjami compliance i dedykowanym wsparciem.
Kod aplikacji jest ten sam. Model wdrożenia się różni. To wymaga dobrej automatyzacji infrastruktury, ale to sprawdzony wzorzec.
Jak wdrożyć model hybrydowy
- Zbuduj aplikację najpierw jako multi-tenant. Cała logika izolacji tenantów pozostaje taka sama niezależnie od modelu wdrożenia.
- Użyj infrastructure-as-code (Terraform, Pulumi lub CDK) do automatyzacji tworzenia środowisk.
- Stwórz pipeline provisionowania, który może uruchomić nowe dedykowane środowisko w minuty, nie dni.
- Utrzymuj jedną bazę kodu. Ten sam obraz Docker działa zarówno w środowiskach współdzielonych, jak i dedykowanych. Konfiguracja (nie kod) określa zachowanie.
Strategie bazodanowe szczegółowo
Baza danych to miejsce, gdzie decyzje o tenancy mają największy wpływ. Oto trzy sprawdzone strategie.
Strategia 1: Współdzielone tabele z tenant_id
Każda tabela ma kolumnę tenant_id. Wszystkie zapytania filtrują po niej.
CREATE TABLE projects (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id UUID NOT NULL REFERENCES tenants(id),
name TEXT NOT NULL,
created_at TIMESTAMPTZ DEFAULT now()
);
CREATE INDEX idx_projects_tenant ON projects(tenant_id);
-- Zawsze odpytuj z kontekstem tenanta
SELECT * FROM projects WHERE tenant_id = $1;
Najlepsze dla: większości produktów SaaS. Proste, kosztowo efektywne, dobrze zrozumiane.
Strategia 2: Schemat per tenant
Każdy tenant dostaje własny schemat PostgreSQL w ramach współdzielonej bazy danych. Tabele mają tę samą strukturę, ale dane są fizycznie rozdzielone.
-- Stwórz schemat dla nowego tenanta
CREATE SCHEMA tenant_abc123;
-- Stwórz tabele w schemacie tenanta
CREATE TABLE tenant_abc123.projects (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
name TEXT NOT NULL,
created_at TIMESTAMPTZ DEFAULT now()
);
-- Ustaw search path per żądanie
SET search_path TO tenant_abc123, public;
-- Teraz zapytania są automatycznie ograniczone
SELECT * FROM projects;
Najlepsze dla: produktów wymagających silniejszej izolacji niż row-level, ale bez kosztu oddzielnych baz. Działa dobrze do kilkuset tenantów.
Strategia 3: Oddzielne bazy danych
Każdy tenant dostaje dedykowaną instancję PostgreSQL (lub dedykowaną bazę na współdzielonym serwerze).
// Routing połączeń bazodanowych
class TenantDatabaseRouter {
private connections: Map<string, DatabasePool> = new Map();
async getConnection(tenantId: string): Promise<DatabasePool> {
if (this.connections.has(tenantId)) {
return this.connections.get(tenantId)!;
}
const config = await this.loadTenantDbConfig(tenantId);
const pool = new DatabasePool({
host: config.host,
port: config.port,
database: config.database,
user: config.user,
password: config.password,
});
this.connections.set(tenantId, pool);
return pool;
}
private async loadTenantDbConfig(tenantId: string): Promise<DbConfig> {
// Wyszukaj dane połączenia bazy tenanta
// z centralnego magazynu konfiguracji
return await configStore.get(`tenants/${tenantId}/database`);
}
}
Najlepsze dla: enterprise SaaS z klientami o wysokiej wartości, surowymi wymaganiami compliance lub obowiązkami rezydencji danych.
Podejmowanie decyzji
Oto prosty framework decyzyjny:
- Jaki jest rozmiar Twojego docelowego klienta? SMB wskazuje na multi-tenant. Enterprise wskazuje na single-tenant lub hybrid.
- Jaki jest Twój cennik? Poniżej 100 EUR/miesiąc potrzebujesz multi-tenant, by być rentownym. Powyżej 1000 EUR/miesiąc single-tenant staje się realne.
- Jakie są wymagania compliance? Regulowane branże często wymagają silniejszej izolacji.
- Ilu tenantów oczekujesz? Setki lub tysiące tenantów wymagają współdzielonej infrastruktury. Dziesięciu do pięćdziesięciu dużych tenantów może działać z dedykowanymi instancjami.
- Jakie są zdolności operacyjne Twojego zespołu? Single-tenant wymaga większych inwestycji DevOps. Multi-tenant jest operacyjnie prostsze.
Jeśli nie jesteś pewien, zacznij od multi-tenant ze współdzieloną bazą danych i row-level security. To najtańsza opcja, jest bezpieczna przy prawidłowej implementacji i utrzymuje prostą architekturę. Zawsze możesz później dodać dedykowaną taryfę dla klientów enterprise.
Podsumowanie
Nie ma uniwersalnie poprawnego modelu tenancy. Właściwy wybór zależy od Twoich klientów, cennika, wymagań compliance i zdolności operacyjnych.
Większość produktów SaaS powinna zacząć od multi-tenant. Jest tańsze, prostsze i dobrze się skaluje. W miarę przechodzenia w górę rynku i zaczynania sprzedaży do większych organizacji, dodawaj opcje single-tenant dla klientów, którzy ich potrzebują i zapłacą odpowiednio.
Architektura powinna służyć biznesowi, nie odwrotnie. Buduj dla klientów, których masz dziś, i projektuj z wystarczającą elastycznością, by obsłużyć klientów, których chcesz jutro.
Potrzebujesz pomocy w wyborze właściwej architektury dla swojego produktu SaaS? Budowaliśmy systemy multi-tenant i single-tenant w różnych branżach. Znajdźmy najlepsze podejście dla Twojego projektu.