← Wróć do artykułów

Dlaczego PostgreSQL to bezkonkurencyjny wybór dla aplikacji Multi-Tenant SaaS

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 izolacjiOpis w PostgreSQLZaletyKiedy stosować
Shared Database, Shared Schema (RLS)Wszyscy najemcy w tych samych tabelach z kolumną tenant_id i polityką RLSNajniższe koszty, banalnie proste migracje, tysiące tenantów na jednej bazieStandardowe platformy SaaS B2B / B2C
Schema-per-TenantKażdy najemca ma dedykowany schemat (CREATE SCHEMA tenant_123) w jednej bazieBardzo proste backupy per tenant, fizyczna separacja obiektówKlienci Enterprise o podwyższonych wymogach zgodności
Database-per-TenantDedykowana baza per klient, zarządzana przez centralny router połączeńMaksymalna izolacja zasobów i CPU/RAMSektor 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:

  1. Gwarancji bezpieczeństwa RLS,
  2. Elastyczności JSONB z indeksami GIN,
  3. 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.