Architektura Multi-Tenancy to fundament każdego rentownego systemu Software-as-a-Service (SaaS). Oznacza ona zdolność pojedynczej instancji aplikacji (oraz bazy danych) do jednoczesnej obsługi wielu niezależnych organizacji (najemców / tenantów) przy zachowaniu bezwzględnej izolacji ich danych.
Podczas gdy na rynku pojawiają się kolejne niszowe silniki bazodanowe, PostgreSQL nieprzerwanie pozostaje złotym standardem dla systemów SaaS. W tym artykule analizujemy funkcje, które czynią Postgresa bezkonkurencyjnym rozwiązaniem w architekturze multi-tenant.
1. Row-Level Security (RLS): Bezpieczeństwo na poziomie jądra bazy
W tradycyjnych bazach danych (np. MySQL) izolacja danych w modelu współdzielonej tabeli (Shared Table) opiera się wyłącznie na dyscyplinie programistów: każde zapytanie SQL w kodzie aplikacji musi zawierać warunek WHERE tenant_id = ?. Wystarczy jedno przeoczenie lub błąd w zapytaniu analitycznym, aby dane poufne jednej firmy wyciekły do panelu innej.
PostgreSQL rozwiązuje ten problem systemowo poprzez mechanizm Row-Level Security (RLS):
-- Włączenie RLS dla tabeli zamówień
ALTER TABLE orders ENABLE ROW LEVEL SECURITY;
-- Wymuszenie polityki również dla właściciela tabeli
ALTER TABLE orders FORCE ROW LEVEL SECURITY;
-- Definicja polityki izolacji w oparciu o zmienną sesyjną
CREATE POLICY tenant_isolation_policy ON orders
AS RESTRICTIVE
USING (tenant_id = NULLIF(current_setting('app.current_tenant_id', true), '')::uuid);
Przed wykonaniem zapytania aplikacja (np. w Go czy Node.js) ustawia w bieżącej transakcji kontekst najemcy:
SET LOCAL app.current_tenant_id = 'a1b2c3d4-e5f6-7890-abcd-ef1234567890';
SELECT * FROM orders; -- Zwróci WYŁĄCZNIE rekordy przypisane do wskazanego tenanta!
Nawet jeśli programista wykona SELECT * FROM orders bez klauzuli WHERE, silnik PostgreSQL automatycznie i nieodwracalnie przefiltruje wiersze. Ryzyko wycieku danych między tenantami zostaje wyeliminowane na poziomie samego silnika relacyjnego.
2. Elastyczność modeli izolacji: Trzy podejścia w jednym silniku
PostgreSQL nie narzuca jednego sztywnego schematu architektonicznego. Umożliwia wdrożenie każdego ze standardowych modeli multi-tenancy w zależności od skali i wymogów regulacyjnych klienta:
| Model izolacji | Opis w PostgreSQL | Zalety | Kiedy stosować |
|---|---|---|---|
| Shared Database, Shared Schema (RLS) | Wszyscy najemcy w tych samych tabelach z kolumną tenant_id i polityką RLS | Najniższe koszty, banalnie proste migracje, tysiące tenantów na jednej bazie | Standardowe platformy SaaS B2B / B2C |
| Schema-per-Tenant | Każdy najemca ma dedykowany schemat (CREATE SCHEMA tenant_123) w jednej bazie | Bardzo proste backupy per tenant, fizyczna separacja obiektów | Klienci Enterprise o podwyższonych wymogach zgodności |
| Database-per-Tenant | Dedykowana baza per klient, zarządzana przez centralny router połączeń | Maksymalna izolacja zasobów i CPU/RAM | Sektor bankowy, medyczny (HIPAA), rygorystyczne SLA |
Co najważniejsze: w PostgreSQL możesz w ramach tej samej platformy łączyć te podejścia - np. 95% mniejszych klientów obsługiwać w schemacie współdzielonym z RLS, a dla 5% największych kontraktów Enterprise wydzielić osobne schematy.
3. JSONB i indeksy GIN: Custom Fields bez naruszania ACID
W systemach SaaS niemal każdy większy klient domaga się „własnych pól” w formularzach, produktach czy zamówieniach (tzw. custom attributes).
W klasycznych relacyjnych bazach danych wymagało to stosowania anty-wzorca EAV (Entity-Attribute-Value) ze skomplikowanymi złączeniami JOIN lub migracji schematu tabeli w locie.
PostgreSQL oferuje binarny format JSONB wspierany przez indeksy GIN (Generalized Inverted Index):
-- Tabela produktów z polami dynamicznymi
CREATE TABLE products (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id UUID NOT NULL,
sku VARCHAR(64) NOT NULL,
custom_attributes JSONB NOT NULL DEFAULT '{}'::jsonb
);
-- Indeks GIN umożliwiający błyskawiczne wyszukiwanie wewnątrz struktur JSONB
CREATE INDEX idx_products_custom_attrs ON products USING GIN (custom_attributes);
-- Wyszukiwanie produktów z niestandardowym atrybutem z indeksem w mikrosekundy:
SELECT * FROM products
WHERE custom_attributes @> '{"color": "navy", "certified": true}';
Otrzymujesz elastyczność bazy dokumentowej (jak MongoDB) przy zachowaniu pełnej spójności transakcyjnej ACID, kluczy obcych i relacji SQL.
4. Deklaratywne partycjonowanie tabel
Gdy baza SaaS rośnie do dziesiątek milionów rekordów, pojedyncze tabele transakcyjne zaczynają cierpieć na degradację wydajności indeksów B-Tree.
PostgreSQL udostępnia wbudowane, deklaratywne partycjonowanie tabel:
- Partycjonowanie według listy (
PARTITION BY LIST (tenant_id)) dla największych najemców o gigantycznym wolumenie danych. - Partycjonowanie według zakresu (
PARTITION BY RANGE (created_at)) dla danych historycznych, audytowych i logów.
Planista zapytań Postgresa stosuje mechanizm Partition Pruning - odrzuca partycje nienależące do danego zapytania, skanując wyłącznie mały wycinek dysku.
Podsumowanie
W itbsv projektujemy zaawansowane systemy SaaS (w tym m.in. marketplace barterowy ReFaber czy platformę cyfrowych paszportów produktów VeriFactum) w oparciu o PostgreSQL.
Połączenie:
- Gwarancji bezpieczeństwa RLS,
- Elastyczności JSONB z indeksami GIN,
- Dojrzałego ekosystemu (wsparcie dla PostGIS, pgvector do integracji AI, pgbouncer)
sprawia, że PostgreSQL jest fundamentem technologicznym, z którego Twoja platforma nie wyrośnie przez kolejne dekady.