Czy system wielodostępny z SQL Server 2016, Shard, czy powinien mieć izolację dzierżawcy za pośrednictwem oddzielnej bazy danych dla każdego dzierżawcy?

12

Biorąc pod uwagę przypadek użycia:

  • Dane najemcy nie powinny przenikać, jeden najemca nie potrzebuje danych innego najemcy.
  • Każdy najemca może potencjalnie mieć duży wolumen danych historycznych.
  • Serwer SQL jest hostowany w instancji AWS EC2.
  • Każdy najemca jest geograficznie odległy.
  • Istnieje zamiar korzystania z zewnętrznych narzędzi wizualizacji, takich jak PowerBI Embedded
  • Oczekuje się, że ilość danych wzrośnie z czasem
  • Koszt systemu jest ograniczony.
  • Rozwiązanie musi być możliwe do utrzymania bez DBA produkcyjnego 24/7
  • Rozwiązanie powinno mieć możliwość skalowania w poziomie.
  • Łączna liczba najemców wynosi mniej niż 50

Jaka byłaby zalecana architektura, czy istnieją jakieś referencyjne implementacje dla tego przypadku użycia? Wierzę, że wiele osób mogło już zmierzyć się z tym problemem podczas tworzenia oprogramowania dla przedsiębiorstw.

Myślę, że jest to inna sytuacja niż obsługa rosnącej liczby najemców w architekturze baz danych dla wielu dzierżawców . Przypadek użycia wspomniany w tym pytaniu dotyczy większej liczby najemców, co bardzo różni się od posiadania bardzo niewielu (50) dużych najemców. Wspomniana architektura może być tutaj rozwiązaniem, o czym chcę wiedzieć więcej.

DS
źródło

Odpowiedzi:

16

Gotcha z shardingiem polega na tym, że aplikacja musi wiedzieć, który fragment ma zostać wysłany. Zasadniczo odbywa się to poprzez dzielenie na coś takiego jak klient. Dostosuję jeden z moich starych postów na blogu, aby używał go jako mojej odpowiedzi.

Podczas budowania aplikacji dla wielu klientów istnieją dwa typowe sposoby projektowania baz danych:

  • Opcja A: Umieść wszystkich klientów w tej samej bazie danych
  • Opcja 2: zbuduj jedną bazę danych na klienta

Umieszczenie wszystkich klientów w tej samej bazie danych

To proste: wystarczy dodać tabelę klienta u góry schematu, dodać tabelę ClientUsers, aby upewnić się, że ludzie widzą tylko własne dane, i gotowe.

Korzyści z tego podejścia:

Łatwiejsze zarządzanie schematem. Gdy programiści wdrażają nową wersję aplikacji, muszą wprowadzać zmiany schematu tylko w jednej bazie danych. Nie ma obaw, że różni klienci nie będą zsynchronizowani lub będą mieli niewłaściwą wersję.

Łatwiejsze dostrajanie wydajności. Możemy sprawdzić wykorzystanie indeksu i statystyki w jednym miejscu, łatwo wdrożyć ulepszenia i natychmiast zobaczyć efekty u wszystkich naszych klientów. W przypadku setek lub tysięcy baz danych koordynacja nawet najmniejszej zmiany może być trudna. Możemy sprawdzać zawartość naszej pamięci podręcznej procedur i mieć pewność, które zapytania lub procedury przechowywane są najbardziej intensywne w całej naszej aplikacji, natomiast jeśli używamy oddzielnych baz danych dla klienta, możemy mieć trudniejsze agregowanie zapytań w różnych planach wykonania.

Łatwiej zbudować zewnętrzny interfejs API. Jeśli musimy przyznać dostęp do całej naszej bazy danych osobom postronnym w celu tworzenia produktów, możemy to zrobić łatwiej, jeśli wszystkie dane znajdują się w jednej bazie danych. Jeśli interfejs API ma do czynienia z grupowaniem danych z wielu baz danych na wielu serwerach, wydłuża czas programowania i testowania. (Z drugiej strony to, że „wiele serwerów” zaczyna wskazywać na ograniczenie scenariusza „jedna baza danych do rządzenia nimi wszystkimi”: jedna baza danych zwykle oznacza, że ​​całe nasze obciążenie wpływa tylko na jeden serwer bazy danych.) W twoim przypadku , dzięki PowerBI, posiadanie wszystkich w jednej bazie danych znacznie ułatwi zarządzanie połączeniami.

Łatwiejsza wysoka dostępność i odzyskiwanie po awarii. Naprawdę bardzo proste jest zarządzanie kopią lustrzaną bazy danych, wysyłaniem dzienników, replikacją i klastrowaniem, jeśli wszystko, o co musimy się martwić, to tylko jedna baza danych. Możemy szybko zbudować infrastrukturę.

Umieszczenie każdego klienta we własnej bazie danych lub odłamku

Nadal potrzebujesz listy klientów, ale teraz staje się ona katalogiem - dla każdego klienta śledzisz również fragment, w którym on żyje. Po uruchomieniu aplikacja wysyła zapytanie do tej tabeli i buforuje ją w pamięci RAM. Gdy potrzebuje danych dla klienta, łączy się bezpośrednio z tym fragmentem (baza danych i serwer).

Korzyści z tego podejścia:

Łatwiejsze przywracanie pojedynczego klienta. Klientami są niewiarygodne worki mięsne. (Z wyjątkiem moich - są niezawodnymi workami z mięsem). Mają wszelkiego rodzaju „ups” momenty, w których chcą odzyskać wszystkie swoje dane z powrotem do punktu w czasie, a to jest ogromny ból z tyłu, jeśli ich dane są pomieszane inne dane klienta w tych samych tabelach. Przywracanie w scenariuszu z pojedynczą bazą danych klienta jest wyjątkowo proste: wystarczy przywrócić bazę danych klienta. Nie dotyczy to nikogo innego.

Łatwiejszy eksport danych. Klienci uwielbiają dostawać swoje dane. Chcą bezpieczeństwa, wiedząc, że mogą wydostać swoje dane w dowolnym momencie, unikając przerażającego scenariusza blokowania dostawcy i chcą tworzyć własne raporty. Po odizolowaniu danych każdego klienta w jego własnej bazie danych, możemy po prostu dać mu kopię własnej kopii zapasowej bazy danych. Nie musimy budować interfejsów API eksportu danych.

Łatwiejsza skalowalność wielu serwerów. Gdy nasza aplikacja potrzebuje więcej mocy, niż możemy uzyskać z jednego serwera, możemy podzielić bazy danych na wiele serwerów. Możemy również rozłożyć obciążenie geograficznie, dzięki czemu serwery w Azji lub Europie będą bliżej klientów.

Łatwiejsze dostrajanie wydajności dla poszczególnych klientów. Jeśli niektórzy klienci używają różnych funkcji lub raportów, możemy zbudować wyspecjalizowany zestaw indeksów lub indeksowanych widoków tylko dla tych klientów bez zwiększania wielkości danych wszystkich osób. To prawda, że ​​istnieje tu pewne ryzyko - ze względu na różnice w schematach między klientami sprawiliśmy, że wdrażanie kodu jest nieco bardziej ryzykowne, a zarządzanie wydajnością trudniejsze.

Łatwiejsze zarządzanie bezpieczeństwem. Tak długo, jak prawidłowo zablokowaliśmy bezpieczeństwo dla jednego użytkownika na bazę danych, nie musimy się martwić, że Klient X uzyska dostęp do danych klienta Y. Jeśli jednak użyjemy tylko jednego loginu dla wszystkich, to tak naprawdę nie rozwiązaliśmy tego problemu.

Łatwiejsze utrzymanie okien. W globalnym środowisku, w którym klienci są rozproszeni po całym świecie, łatwiej jest przenieść klientów offline w celu konserwacji, jeśli możemy to zrobić w grupach lub strefach.

Który do ciebie pasuje?

Nie ma jednego właściwego wyboru: musisz poznać mocne i słabe strony swojej firmy. Weźmy za przykład dwóch moich klientów.

Firma A specjalizuje się w dostrajaniu wydajności sprzętu. Są naprawdę, bardzo dobrzy w wyciskaniu ostatniej wydajności sprzętu i nie mają nic przeciwko wymianie sprzętu SQL Server w cyklu 12-18 miesięcy. (Odświeżają serwery internetowe co 4-6 miesięcy!) Ich piętą achillesową są ekstremalne wymagania dotyczące zgodności i bezpieczeństwa. Mają niewiarygodne potrzeby kontrolne, a im łatwiej jest wdrożyć kuloodporne kontrole na jednym serwerze, jednej bazie danych, niż zarządzać tymi wymaganiami w tysiącach baz danych na dziesiątkach serwerów. Wybrali jedną bazę danych, jeden serwer, wielu klientów.

Firma 2 przoduje w praktykach programistycznych. Zarządzanie zmianami schematu i wdrażaniem kodu w tysiącach baz danych po prostu nie stanowi dla nich problemu. Mają klientów na całym świecie i przetwarzają transakcje kartą kredytową dla tych klientów przez całą dobę. Potrzebują zdolności do geograficznego rozkładania obciążenia i nie chcą wymieniać serwerów na całym świecie co 12-18 miesięcy. Wybrali jedną bazę danych dla każdego klienta, a to się opłaca, gdy zaczynają umieszczać serwery SQL w Azji i Europie dla swoich zagranicznych klientów.

Brent Ozar
źródło
„W twoim przypadku, dzięki PowerBI, posiadanie wszystkich w jednej bazie danych znacznie ułatwi zarządzanie połączeniami”. W tej chwili PowerBI Embedded nie ma zabezpieczeń na poziomie wiersza, dlatego każdy lokator w jednej bazie danych budzi wątpliwości co do tego przypadku użycia, patrz: community.powerbi.com/t5/Developer/... , w świetle tych informacji, czy mógłbyś przeformułować to lub zasugerować alternatywę lub poprawić moje zrozumienie?
DS
Również „Umieszczenie każdego klienta we własnej bazie danych lub odłamku” może wyjaśnić różnicę między tymi dwiema sugestiami
DS
Powiem tylko, że konieczność wdrożenia na więcej niż jednej bazie danych nie jest tak zła, jak się wydaje. W 2017 roku mamy wiele opcji, które bardzo ułatwiają wdrażanie zmian w 1, 5 lub 900 bazach danych. A kiedy masz wyjątki dla określonych klientów, zwykle można je wprowadzić do tych baz danych w taki sposób, aby nie zakłócały wspólnego kodu.
Aaron Bertrand
5

Jeszcze jedna uwaga, której jeszcze nie widziałem w innych odpowiedziach.

Posiadanie projektu, który pozwala wielu najemcom w jednej bazie danych, zapewni elastyczność później. Jeśli ładowanie / skalowanie w górę / zabezpieczenia / lokalizacja geograficzna będą później sugerować, że najemca powinien mieć oddzielną bazę danych, można ją utworzyć, przywracając poprawną bazę danych w nowej instancji. Dane pozostałych najemców są nadal chronione przez istniejące mechanizmy. Teraz przestarzałe dane można usuwać fragmentarycznie ze starych i nowych baz danych w miarę upływu czasu.

Odwrotna sytuacja nie jest prawdą. Konsolidacja wielu baz danych dla jednego dzierżawcy będzie wymagać znacznie więcej pracy.

Michael Green
źródło
4

Jedną z praktyk, która sprawia, że ​​modele z wieloma dzierżawcami są znacznie łatwiejsze, nawet jeśli psuje normalizację *, jest umieszczenie kolumny dla każdego najemcy. Możesz to nazwać TenantID. W ten sposób każde zapytanie uruchomione w bazie danych może filtrować według TenantID w każdej tabeli, a partycjonowanie bazy danych umożliwia izolowanie danych dla każdego dzierżawcy i przyspieszenie zapytań dzięki dopasowanym partycjom. Znacznie łatwiej jest mieć w ten sposób wszystkich najemców w jednej bazie danych.

* Nie zawsze łamie normalizację, ale może. Na przykład, jeśli masz Personi do PersonAddressstołu. PersonStół będzie miał TenantID, PersonIDjako klucz podstawowy. PersonAddressStół będzie miał TenantID, PersonID, AddressTypeIDjako klucz podstawowy z tym, co ja sugeruję.

Normalnie PersonIDwystarczyłoby, ponieważ możesz dołączyć to z powrotem do Personstołu, aby znaleźć Tenant. Sugeruję, abyś przeniósł TenantIDsię do każdego kolejnego stołu, nawet jeśli cieńszy klucz będzie działał.

Rozumiałem, że przekazanie jakichkolwiek informacji do tabeli, które można by uzyskać z innych danych, uznano za przełamanie normalizacji. Ale być może używanie cienkich klawiszy jest po prostu najlepszą praktyką.

Matthew Sontum
źródło
Dzięki, zgadzam się z sugestią i na dodatek, chciałbym wspomnieć, że to pole TenantID musi być liczbą całkowitą, a nie GUID, w ten sposób zostaliśmy spaleni w celu zwiększenia wydajności.
DS
3
Ale nawet jeśli zdecydujesz się przenieść TenantID do tabel podrzędnych, czego nie musisz robić, szerszy klucz nie oznacza, że ​​normalizacja jest „zepsuta”. Podobnie jak wybranie GUID zamiast TOŻSAMOŚCI (szerszy klucz) nie łamie normalizacji, podobnie jak wybór szerszego naturalnego klucza zamiast używania surogatów.
Aaron Bertrand