Multi-tenancy — schemat czy row-level security
Aplikacja obsługująca wielu klientów w jednej instalacji musi rozstrzygnąć jedno pytanie: jak odseparować ich dane. Odpowiedź wpływa na koszt infrastruktury, czas przywracania kopii zapasowej pojedynczego klienta i na to, czy przejdziesz audyt u klienta korporacyjnego. Zmiana tej decyzji po dwóch latach oznacza przepisanie warstwy danych.
Krótka odpowiedź: dla większości produktów właściwym wyborem jest wspólna baza z identyfikatorem najemcy i wymuszonym filtrowaniem — najniższy koszt utrzymania i najlepsze skalowanie przy dużej liczbie klientów.
Osobny schemat wybieraj przy kilkudziesięciu do kilkuset klientach o dużej wartości, gdzie liczy się możliwość odtworzenia danych pojedynczego klienta. Osobną bazę tylko wtedy, gdy wymusza to audyt lub regulacja.
Trzy modele izolacji
| Model | Izolacja | Koszt infrastruktury | Sensowna skala | Odtworzenie danych klienta |
|---|---|---|---|---|
| Wspólna tabela | Logiczna, w kodzie lub bazie | Najniższy | Od kilkuset do dziesiątek tysięcy | Trudne — wymaga eksportu wybiórczego |
| Osobny schemat | Na poziomie bazy danych | Średni | Do kilkuset | Proste — kopia jednego schematu |
| Osobna baza | Pełna | Najwyższy | Do kilkudziesięciu | Trywialne |
Różnica w koszcie nie sprowadza się do rachunku za serwery. Przy osobnych bazach każda zmiana struktury danych musi zostać wykonana tyle razy, ilu jest klientów — a jeśli któraś się nie powiedzie, powstają dwie różne wersje aplikacji działające jednocześnie. To obciążenie operacyjne rośnie liniowo i szybko staje się głównym kosztem.
Wspólna tabela — jak zrobić to bezpiecznie
Model najtańszy jest jednocześnie najbardziej podatny na błąd o poważnych konsekwencjach: pominięcie filtra po identyfikatorze najemcy w jednym zapytaniu oznacza pokazanie danych innego klienta. Dyscyplina programistów nie jest wystarczającym zabezpieczeniem, bo wystarczy jedno przeoczenie w setkach zapytań.
Rozwiązaniem jest przeniesienie odpowiedzialności na warstwę, która nie zapomina. Są dwa sprawdzone podejścia i najlepiej stosować oba naraz.
- Zabezpieczenie na poziomie wierszy w bazie danych — reguła przypisana do tabeli filtruje wiersze na podstawie parametru sesji. Zapytanie bez ustawionego kontekstu nie zwraca niczego.
- Wymuszony filtr w warstwie dostępu do danych — repozytorium, które nie pozwala zbudować zapytania bez wskazania najemcy.
- Kontekst ustawiany w jednym miejscu — na wejściu żądania, na podstawie sesji użytkownika, nigdy na podstawie parametru przesłanego przez przeglądarkę.
- Testy sprawdzające izolację — automatyczny scenariusz próbujący sięgnąć po rekord innego najemcy, uruchamiany przy każdej zmianie.
- Klucze złożone zawierające identyfikator najemcy, żeby błędne odwołanie kończyło się brakiem rekordu.
Dlaczego zabezpieczenie na poziomie bazy jest ważniejsze niż w kodzie. Do bazy sięga nie tylko aplikacja. Zapytania raportowe, skrypty migracyjne, narzędzia analityczne i doraźne poprawki wykonywane ręcznie omijają warstwę aplikacji w całości. Reguła zapisana w bazie obowiązuje wszystkie te ścieżki jednakowo.
Osobny schemat — kompromis dla klientów o dużej wartości
Każdy klient ma własny zestaw tabel w tej samej bazie. Struktura jest identyczna, dane w pełni rozdzielone na poziomie silnika bazy danych. To rozwiązanie ma trzy zalety, które w sprzedaży do większych organizacji bywają decydujące.
- Odtworzenie danych jednego klienta nie wymaga wybiórczego eksportu z tabel zbiorczych.
- Przekazanie danych przy rozstaniu sprowadza się do zrzutu jednego schematu.
- Argument w rozmowie o bezpieczeństwie — separacja na poziomie bazy jest łatwiejsza do wykazania w audycie niż filtr w kodzie.
Kosztem jest obsługa zmian struktury. Przy dwustu schematach każda migracja to dwieście operacji, które muszą zakończyć się powodzeniem albo zostać cofnięte. Wymaga to narzędzia uruchamiającego migracje sekwencyjnie, z rejestrem stanu każdego schematu i możliwością wznowienia po przerwaniu.
Osobna baza — kiedy naprawdę jest potrzebna
Ten wariant wybiera się z powodów regulacyjnych albo handlowych, rzadko technicznych. Uzasadniają go wymóg przechowywania danych w określonej lokalizacji, konieczność użycia klucza szyfrującego kontrolowanego przez klienta lub zapis w umowie o pełnej separacji infrastruktury.
Warto policzyć koszt przed złożeniem takiej obietnicy. Osobna baza dla każdego klienta oznacza osobne kopie zapasowe, osobny monitoring, osobne okna serwisowe i wielokrotnie większy nakład na wdrażanie zmian. W praktyce model ten stosuje się dla kilkunastu największych klientów, obok wspólnej instalacji dla pozostałych.
Model mieszany
Najczęściej spotykana architektura w dojrzałych produktach nie jest jednorodna. Podstawowa instalacja działa na wspólnej bazie i obsługuje większość klientów, a osobne środowiska tworzone są na życzenie tych, którzy za to płacą.
Warunkiem powodzenia jest jeden kod obsługujący oba warianty i identyczny proces wdrażania zmian. Jeżeli klient korporacyjny dostaje „własną wersję", która zaczyna się rozjeżdżać z główną, po roku utrzymujesz dwa produkty w cenie jednego.
| Sytuacja | Rekomendacja |
|---|---|
| Produkt dla małych firm, tysiące kont | Wspólna tabela z zabezpieczeniem na poziomie wierszy |
| Narzędzie dla średnich firm, kilkaset kont | Wspólna tabela; osobny schemat przy wymogach audytowych |
| Sprzedaż do korporacji, kilkadziesiąt wdrożeń | Osobny schemat, opcjonalnie osobna baza dla największych |
| Dane medyczne lub finansowe pod regulacją | Osobna baza, szyfrowanie kluczem klienta |
| Wersja pilotażowa przed pierwszą sprzedażą | Wspólna tabela — najprostsza i najszybsza do zbudowania |
Elementy, o których łatwo zapomnieć
- Pliki i załączniki — separacja w bazie nie chroni danych, jeśli pliki leżą we wspólnym katalogu z przewidywalnymi nazwami.
- Wyszukiwarka pełnotekstowa — zewnętrzny indeks musi mieć własny mechanizm filtrowania po najemcy.
- Pamięć podręczna — klucz bez identyfikatora najemcy oznacza pokazanie cudzych danych z bufora.
- Rejestr zdarzeń — powinien zawierać najemcę, inaczej diagnostyka zgłoszenia jest niemożliwa.
- Zadania cykliczne — muszą wykonywać się w kontekście każdego najemcy osobno.
- Eksport i raporty — najczęstsze miejsce, w którym filtr zostaje pominięty.
- Środowisko testowe — dane produkcyjne jednego klienta nie mogą trafić do testów.
Migracje struktury przy wielu najemcach
W aplikacji obsługującej jednego klienta zmiana struktury bazy jest czynnością rutynową. Przy kilkuset najemcach staje się operacją, którą trzeba zaplanować — zwłaszcza gdy aplikacja ma działać bez przerwy.
Kluczowa zasada brzmi: zmiany muszą być zgodne wstecz. W trakcie wdrożenia część ruchu obsługuje jeszcze poprzednia wersja kodu, a część nowa, więc struktura danych musi działać z obiema.
- Dodanie kolumny jako operacja osobna, przed zmianą kodu, z wartością domyślną.
- Zapis do obu miejsc w okresie przejściowym, gdy dane są przenoszone.
- Usunięcie starej kolumny dopiero po pełnym wdrożeniu nowej wersji, w osobnym kroku.
- Migracje danych w partiach, nie jednym zapytaniem obejmującym miliony wierszy.
- Test na kopii odpowiadającej wielkością produkcji, bo czas wykonania rośnie nieliniowo.
Przy modelu z osobnym schematem dla każdego klienta dochodzi problem częściowego powodzenia: migracja przechodzi dla stu schematów, a na sto pierwszym kończy się błędem. Potrzebny jest wtedy rejestr stanu każdego schematu i możliwość wznowienia od miejsca przerwania, a nie od początku.
Podsumowanie
Wybór modelu izolacji jest decyzją długoterminową, ale nie musi być trudną. Dla zdecydowanej większości produktów właściwy jest model najprostszy — wspólna baza z identyfikatorem najemcy — pod warunkiem, że filtrowanie wymuszone jest na poziomie bazy danych, a nie pozostawione uwadze programisty.
Osobny schemat i osobna baza rozwiązują konkretne problemy: odtwarzanie danych pojedynczego klienta i wymogi audytowe. Kosztują za to wielokrotnie więcej pracy operacyjnej przy każdej zmianie struktury. Rozsądna strategia to start na modelu wspólnym i dołożenie wariantu izolowanego dopiero wtedy, gdy pojawi się klient, który za niego zapłaci.
Najczęstsze pytania
Wspólną bazę z identyfikatorem najemcy. Jest najprostsza do zbudowania, najtańsza w utrzymaniu i skaluje się do dziesiątek tysięcy kont. Warunkiem bezpieczeństwa jest wymuszenie filtrowania na poziomie bazy danych, a nie poleganie wyłącznie na tym, że programista pamięta o warunku w każdym zapytaniu.
Dla większości audytów tak, o ile potrafisz wykazać, że reguła obowiązuje wszystkie ścieżki dostępu do danych i że istnieją testy automatyczne weryfikujące izolację. Część klientów korporacyjnych mimo to wymaga separacji fizycznej — wtedy praktycznym rozwiązaniem jest osobny schemat lub baza tylko dla nich.
Potrzebne jest narzędzie uruchamiające migracje sekwencyjnie dla każdego schematu, z rejestrem stanu i możliwością wznowienia po przerwaniu. Kluczowe jest też to, żeby zmiany były zgodne wstecz — w trakcie migracji część klientów działa już na nowej strukturze, a część jeszcze na starej.
To najczęściej pomijany element separacji. Pliki powinny być przechowywane w katalogach lub przestrzeniach przypisanych do najemcy, z nazwami niemożliwymi do odgadnięcia, a dostęp do nich musi przechodzić przez tę samą kontrolę uprawnień co dane w bazie. Bezpośredni odnośnik do pliku bez weryfikacji uprawnień unieważnia całą separację.
Da się, ale to kosztowna operacja obejmująca przepisanie warstwy dostępu do danych i migrację produkcyjną z przestojem. Przejście ze wspólnej bazy na osobne schematy jest wykonalne, odwrotny kierunek trudniejszy ze względu na kolizje identyfikatorów. Dlatego decyzję warto podjąć świadomie na starcie, nawet jeśli wybór padnie na najprostszy wariant.
Convert Studio realizuje projekty z tego obszaru dla firm w całej Polsce. Zobacz: Aplikacje webowe → · Lokalizacje →
Bezpłatna wycena Twojego projektu
Opisz w kilku zdaniach, co chcesz zbudować — stronę, aplikację czy sklep. Odpowiadamy w ciągu 24 godzin roboczych konkretną propozycją zakresu i harmonogramu. Konsultacja i wycena są bezpłatne, bez zobowiązań.