Jest to pierwszy artykuł z serii Aplikacje multi-tenant w PHP, w której będę opisywał różne zagadnienia dotyczące tej architektury. W dzisiejszym wpisie przedstawię różne strategie składowania danych w tym podejściu. W kolejnych wpisach postaram się omówić inne aspekty takie jak dynamiczna konfiguracja, tworzenie instancji, deployment, dedykowane rozwiązania.
Aplikacje multi-tenant
Minęły czasy, w których aby wysłać maila do tysięcy odbiorców musieliśmy sami pisać cały system newslettera, a żeby wystawić fakturę potrzebowaliśmy zainstalować program do fakturowania. Teraz te i masę innych rzeczy jesteśmy w stanie zrobić z każdego miejsca na Ziemi i z dowolnego komputera za pomocą kilku kliknięć, a na każdy nasz problem przypada kilku, kilkunastu czy nawet kilkudziesięciu dostawców danej usługi.
Dawniej z reguły kupowaliśmy licencję na dany software i instalowaliśmy go na swoim komputerze czy serwerze w sieci. Dzisiaj kupujemy dostęp do usługi, zamiast licencji otrzymujemy login i hasło czy klucz API pozwalające na korzystanie z aplikacji providera.
Tego typu aplikacje są udostępniane w modelu SaaS (Software as a Service) i oparte o architekturę multi-tenant polegającą na obsłudze wielu dzierżawców (tenantów) za pomocą jednej instancji aplikacji. Na podstawie requestu (np. subdomeny, klucza API, loginu) aplikacja rozpoznaje który z tenantów będzie jej używał i odpowiednio się konfiguruje tak aby korzystać z jego danych, ustawień a nawet plików z dedykowanymi mu rozwiązaniami.
Poniżej przedstawiam 3 sposoby na to jak możemy przechowywać dane klientów oraz wady i zalety każdego z rozwiązań.
Jedna baza danych dla wszystkich
Dane wszystkich klientów trzymamy we wspólnej bazie danych, w każdej tabeli posiadamy kolumnę zawierającą jego identyfikator.
Tabela tenants zawierająca listę tenantów w aplikacji.
Tabela products zawierająca produkty wszystkich tenantów, każdy rekord zawiera odpowiednie tenant_id.
Plusy:
- proste w utrzymaniu, nie trzeba tworzyć nowych baz dla kolejnych klientów,
- podczas deploymentu zmieniamy schemat tylko jednej bazy.
Minusy:
- konieczność przechowywania ID tenanta,
- jeśli gdzieś zapomnimy sprawdzić ID może to skutkować dostępem do danych innych klientów, a nawet ich usunięciem,
- wraz ze wzrostem liczby klientów mogą pojawić się problemy z wydajnością.
Wspólna baza z listą tenantów oraz kilka baz z danymi klientów
Rozwiązanie jest podobne do Jedna baza dla wszystkich z tą różnicą, że mamy kilka baz danych:
- baza zawierająca tabelę tenants,
- bazy z danymi klientów – możemy rozlokować instancje na kilku bazach np. grupując wiele „małych” instancji w jednej w bazie, a „duże” w dedykowanych tylko dla nich.
Tabela tenants umieszczona jest teraz w osobnej bazie (np. db_common) oraz dodatkowo przechowuje informacje o tym w której bazie danych znajdują się dane instancji.
Tabela products w bazie db_1
Tabela products w bazie db_2
Struktura tabeli products nie zmienia się jednak dane są rozmieszczone w kilku bazach.
Plusy:
- rozwiązanie jest skalowalne, możemy przenosić klientów do dedykowanych baz danych a nawet na osobne serwery (oczywiście musielibyśmy jeszcze przechowywać host do serwera BD),
- poprawiło się bezpieczeństwo, dane są podzielone na więcej baz.
Minusy:
- nadal w razie błędu programisty istnieje możliwość wymieszania się danych różnych instancji,
- musimy zadbać o utrzymanie schematu w wielu bazach,
- w aplikacji mamy 2 połączenia do różnych baz.
Jedna baza – jeden klient
W tym podejściu każdy klient ma swoją dedykowaną bazę danych, oprócz tego tak jak w poprzednim przykładzie istnieje jedna baza wspólna z informacjami o instancjach oraz nazwach baz.
Tabela tenants.
Tabela products w bazie db_1
Tabela products w bazie db_2.
W związku z tym, że w każdej bazie są dane tylko jednego klienta nie ma potrzeby przechowywania jego identyfikatora.
Plusy:
- brak konieczności przechowywania ID tenanta w każdym rekordzie i podawania go w każdym zapytaniu,
- bezpieczeństwo danych, nie zachodzi możliwość dostępu do danych innych klientów,
- lepsza wydajność, dużo mniej rekordów w tabelach,
- ułatwione debugowanie.
Minusy:
- tworząc nową instancję musimy stworzyć nową bazę danych, schemat i zapewne również uzupełnić ją podstawowymi danymi potrzebnymi aplikacji,
- musimy zadbać o utrzymanie schematu w wielu bazach
- w aplikacji mamy 2 połączenia do różnych baz,
- większe zużycie zasobów,
- niezależnie od tego jak dużo danych zawiera instancja i tak musi mieć dedykowaną bazę.
Co wybrać?
Jak widać nie ma złotego środka i jak to zwykle w programowaniu bywa, odpowiedź na pytanie „co wybrać?” brzmi „to zależy”. Każde rozwiązanie ma swoje plusy i minusy, należy więc dokładnie przeanalizować nasze potrzeby i wymagania naszych klientów, jest to bowiem jedna z decyzji, z której może być trudno się wycofać.
W kolejnej części zajmiemy się praktyką, implementacją tych metod w PHP, a konkretnie we frameworku Symfony z użyciem Doctrine.