Dlaczego tak źle jest czytać dane z bazy danych „będącej własnością” innej mikrousługi


64

Niedawno przeczytałem ten znakomity artykuł na temat architektury mikrousług: http://www.infoq.com/articles/microservices-intro

Stwierdza, że ​​po załadowaniu strony internetowej na Amazon, ponad 100 mikrousług współpracuje w celu obsługi tej strony.

W tym artykule opisano, że cała komunikacja między mikrousługami może odbywać się wyłącznie za pośrednictwem interfejsu API. Moje pytanie brzmi: dlaczego tak źle jest powiedzieć, że wszystkie zapisy bazy danych mogą przechodzić tylko przez interfejs API, ale można czytać bezpośrednio z baz danych różnych mikrousług. Można na przykład powiedzieć, że tylko kilka widoków bazy danych jest dostępnych poza mikrousługą, dzięki czemu zespół obsługujący mikrousługę wie, że tak długo, jak utrzymują te widoki nienaruszone, mogą zmieniać strukturę bazy danych swojej mikro usługi chcieć.

Czy coś mi umyka? Czy istnieje jakiś inny powód, dla którego dane powinny być odczytywane tylko przez interfejs API?

Nie trzeba dodawać, że moja firma jest znacznie mniejsza niż Amazon (i zawsze będzie), a maksymalna liczba użytkowników, jaką możemy mieć, to około 5 milionów.


Jeszcze jednym ogólnym czynnikiem nie wymienionym w odpowiedziach jest to, że po zapisaniu w bazie danych lokalne buforowanie, a nawet proste mapowanie O / R, może dawać nieaktualne dane podczas natychmiastowego uzyskiwania dostępu do bazy danych. Jeśli rozważasz ominięcie API µservice ze względu na szybkość, być może zbyt daleko posunęła się architektura µservice.
Joop Eggen

Po zezwoleniu na odczyt danych z bazy danych każdy szczegół bazy danych staje się częścią publicznego interfejsu API. Nie chciałbym zachować zgodności z tak złożonym API.
Patrick,

Ale w takim przypadku widok nie staje się po prostu częścią interfejsu API, przynajmniej do celów semantycznych? To tylko kwestia tego, co nazywasz API i co zmusza cię do utrzymania. (Zwykle warstwa nad bazą danych jest łatwiejsza do zachowania spójności.)
lc.

Zgadzam się, że widok byłby po prostu formą interfejsu API. Myślę, że większość osób odpowiadających na to pytanie nie czyta mojego pomysłu na temat używania widoków jako poziomu abstrakcji. Rozumiem jednak, że utrzymanie widoku w nienaruszonym stanie, jeśli ktoś zmieni technologię bazy danych, będzie dużym wyzwaniem, ale jestem gotów się założyć, że nie będziemy musieli zmieniać naszej technologii baz danych przez następne 5 lat. Również wydajność nie będzie stanowić problemu tylko dla 5 milionów użytkowników. Biorąc pod uwagę naszą wielkość, dziękuję, że wybiorę to rozwiązanie, mimo że odpowiedzi tutaj wydają się wskazywać, że zmierzam prosto do świata bólu.
David

Odpowiedzi:


69

Bazy danych nie są zbyt dobre w ukrywaniu informacji, co jest całkiem prawdopodobne, ponieważ ich zadaniem jest ujawnianie informacji. Ale to czyni je kiepskim narzędziem, jeśli chodzi o enkapsulację. Dlaczego chcesz enkapsulacji?

Scenariusz: wiążesz kilka komponentów bezpośrednio z RDBMS i widzisz, że jeden konkretny komponent staje się „wąskim gardłem”, dla którego możesz chcieć denormalizować bazę danych, ale nie możesz tego zrobić, ponieważ wpłynęłoby to na wszystkie inne komponenty. Możesz nawet zdać sobie sprawę, że lepiej byłoby ze sklepem z dokumentami lub bazą danych grafów niż z RDBMS. Jeśli dane są enkapsulowane przez mały API, masz realistyczną szansę na reimplementację wspomnianego API w dowolny sposób. Możesz przezroczyście wstawiać warstwy pamięci podręcznej, a co nie.

Grzebanie w warstwie magazynującej bezpośrednio z warstwy aplikacyjnej jest diametralnym przeciwieństwem tego, co sugeruje zasada inwersji zależności .


1
To świetny punkt! Jedną rzeczą, która mnie mniej martwi, jest to, że Postgres obsługuje teraz zarówno magazyn dokumentów, jak i RDBMS ( beztheloop.com/articles/2014-09-30-postgresql-nosql ). Twój punkt jest jednak nadal aktualny i rozważę go dokładnie.
David

3
Nie powinno cię to mniej martwić @David tylko dlatego, że narzędzie może coś zrobić, nie oznacza, że ​​zmiana go nie zepsuje wielu rzeczy. Chodzi o to, by mieć pewien stopień separacji - możesz całkowicie zmienić dane za interfejsem API bez zmiany tego, co widzi użytkownik. Mówię tutaj jako administrator danych ... tak długo, jak klient widzi to samo, możesz zmienić backend tak bardzo, jak chcesz.
Ben,

1
@David Chociaż jest to interesująca wiadomość, nie ma ona znaczenia dla opisanego przeze mnie scenariusza. Jeśli zmienisz schemat DB z relacyjnego na oparty na dokumencie, będzie on miał taki sam wpływ na wszystkie zależne od niego: będziesz musiał przepisać wszystkie zapytania. Koszmar wiąże się z koniecznością wdrożenia wszystkich tych zmian jednocześnie, aby zachować zgodność między komponentami w całym systemie.
back2dos

1
@David: Ograniczenie dostępu do kilku dobrze zdefiniowanych widoków prawdopodobnie oznacza zbudowanie innego API, z pewnymi korzyściami, które się z nim wiążą. Dopóki są to tylko widoki, jesteś jednak ograniczony do dostępu tylko do odczytu. A posiadanie komponentu zależy zarówno od interfejsu API usługi, jak i interfejsu API widoku, co czyni go bardzo delikatnym. Jeśli więc uzależniasz komponent od widoków, z góry określiłeś go jako tylko do odczytu lub wymagający dużej konserwacji. Czuję tutaj dług techniczny. Możesz także dodać partycjonowanie poziome w sposób, który nie pozwala na to Twoja baza danych.
back2dos

2
@David: Na dłuższą metę ważniejsze jest to, jak łatwo jest zmienić kod, niż jak łatwo go napisać. Architektura nie mówi „nie powinieneś pisać kodu w taki i taki sposób”, mówi „jeśli to zrobisz, będziesz cierpiał straszne koszmary próbując go utrzymać”. Jeśli mówisz o prototypach, konserwacja nie jest wymagana. Śmiało. Prototyp powinien udowodnić punkt tak łatwo, jak to możliwe. Ale kiedy próbujesz zintegrować wszystkie sprawdzone punkty z systemem bez przekształcenia go w syzyfowe tortury z własnej woli, lepiej posunąć się o krok dalej.
back2dos

55

Co jest ważniejsze i ważniejsze w mikroserwisie: jego interfejs API lub schemat bazy danych? API, ponieważ taka jest jego umowa z resztą świata. Schemat bazy danych jest po prostu wygodnym sposobem przechowywania danych zarządzanych przez usługę, miejmy nadzieję zorganizowanym w sposób optymalizujący wydajność mikrousług. Zespół programistów powinien mieć swobodę reorganizacji tego schematu - lub przejścia na zupełnie inne rozwiązanie magazynu danych - w dowolnym momencie. Reszta świata nie powinna się tym przejmować. Reszta świata dba o zmiany API, ponieważ API jest umową.

Teraz, jeśli zajrzysz do ich bazy danych

  • Dodajesz niepożądaną zależność od ich schematu. Nie mogą tego zmienić bez wpływu na twoją usługę.
  • Dodasz niechciane i nieprzewidywalne obciążenia do ich elementów wewnętrznych.
  • Na wydajność Twojej usługi będzie miała wpływ wydajność jej bazy danych (będą oni próbowali zoptymalizować swoją usługę, aby dobrze działała dla klientów, a ich baza danych, aby działała dobrze tylko dla ich usług)
  • Wiązujesz swoją implementację ze schematem, który może nie dokładnie i wyraźnie reprezentować zasoby w ich magazynie danych - może zawierać dodatkowe szczegóły, które są potrzebne tylko do śledzenia stanu wewnętrznego lub spełnienia ich konkretnej implementacji (na co nie należy się przejmować).
  • Możesz nieświadomie zniszczyć lub zepsuć stan ich usług (i nie będą wiedzieć, że to robisz)
  • Możesz aktualizować / usuwać / usuwać zasoby z ich bazy danych bez wiedzy o tym.

Dwa ostatnie punkty mogą się nie zdarzyć, jeśli masz dostęp tylko do odczytu, ale pozostałe punkty to więcej niż wystarczający powód. Współużytkowane bazy danych to zła rzecz.

Mniej doświadczeni programiści (lub ci, którzy się nie uczą) często postrzegają bazę danych jako ważniejszą od usługi, postrzegają bazę danych jako prawdziwą rzecz, a usługa tylko sposobem na dotarcie do niej. To jest odwrotna sytuacja.


4
Pamiętaj, że wszystkie powyższe punkty są ważne, nawet jeśli jesteś jedynym programistą pracującym nad całym projektem. Zarządzanie złożonym projektem, nawet samemu, budzi wszystkie te obawy.
itsbruce

15

Architektura Microservice jest trudna do opisania, ale najlepszym sposobem na jej rozważenie jest połączenie architektury zorientowanej na komponenty i architektury zorientowanej na usługi. Oprogramowanie jako pakiet składa się z wielu komponentów dla małych firm z bardzo specyficzną odpowiedzialnością domeny biznesowej. Ich interfejs do świata zewnętrznego w ramach świadczonych usług lub wymaganych usług odbywa się za pośrednictwem interfejsu API jasno określonych usług.

Pisanie do, a nawet czytanie z bazy danych znajdującej się poza domeną biznesową komponentów jest niezgodne z tym stylem architektury.

Głównym tego powodem jest to, że interfejs API dostarczany za pośrednictwem usługi przez inny komponent oprogramowania ma uzasadnione oczekiwania, że ​​interfejs API najprawdopodobniej będzie kompatybilny wstecz, gdy pojawią się nowe wersje komponentu świadczącego usługę. Jeśli jestem programistą komponentu „zapewniającego”, muszę się tylko martwić kompatybilnością wsteczną z moim interfejsem API. Jeśli wiem, że istnieją trzy inne zespoły programistów, które pisały niestandardowe zapytania bezpośrednio do mojej bazy danych, moja praca stała się znacznie bardziej skomplikowana.

Co gorsza, być może ten inny zespół, który to napisał, jest w trakcie sprintu w krytycznym projekcie i nie może teraz zaakceptować tej zmiany z twojego komponentu. Teraz rozwój oprogramowania dla twojego komponentu w domenie biznesowej, którą posiadasz, jest napędzany przez rozwój w innej domenie biznesowej.

Pełna interakcja za pośrednictwem usług zmniejsza sprzężenie między różnymi komponentami oprogramowania, więc takie sytuacje nie występują tak często. Jeśli chodzi o inne komponenty korzystające z widoku w bazie danych, masz większą możliwość, aby widok był kompatybilny wstecz, jeśli ktoś napisałby przeciwko niemu zapytania. Nadal uważam jednak, że powinien to być wyjątek i należy to zrobić tylko w przypadku raportowania lub przetwarzania wsadowego, w którym aplikacja będzie musiała odczytać ogromne ilości danych.

Oczywiście działa to dobrze w dużych rozproszonych zespołach, w których zespoły programistyczne są oddzielone przez domenę biznesową, taką jak Amazon. Jeśli jesteś małym sklepem programistycznym, nadal możesz skorzystać z tego modelu, zwłaszcza jeśli chcesz szybko przygotować się do dużego projektu, ale także, jeśli masz do czynienia z oprogramowaniem dostawcy.


4

W ciągu ostatnich 20 lat widziałem kilka dużych modułowych projektów baz danych i widziałem scenariusz sugerowany przez Davida już kilka razy, w którym aplikacje mają dostęp do zapisu do własnego schematu / zestawu tabel i dostęp do odczytu do innego schematu / zestaw tabel. Najczęściej te dane, do których aplikacja / moduł uzyskuje dostęp tylko do odczytu, można opisać jako „dane podstawowe” .

W tym czasie nie widziałem problemów, które sugerują wcześniejsze odpowiedzi, powinienem był je zobaczyć, dlatego uważam, że warto przyjrzeć się bardziej szczegółowo kwestiom poruszonym w poprzednich odpowiedziach.

Scenariusz: wiążesz kilka komponentów bezpośrednio z RDBMS i widzisz, jak jeden konkretny komponent staje się szyjką butelki

Zgadzam się z tym komentarzem, z wyjątkiem tego, że jest to również argument za lokalną kopią danych do odczytania przez mikrousługę. Oznacza to, że większość dojrzałych baz danych obsługuje replikację, więc bez wysiłku programisty „dane podstawowe” można fizycznie replikować do bazy danych mikrousług, jeśli jest to pożądane lub potrzebne.

Niektórzy mogą rozpoznać to w starszych postaciach jako „korporacyjna baza danych” replikująca podstawowe tabele do „departamentalnej bazy danych”. Chodzi o to, że ogólnie dobrze jest, jeśli baza danych robi to za nas z wbudowaną replikacją zmienionych danych (tylko delty, w formie binarnej i przy minimalnym koszcie do źródłowej bazy danych).

I odwrotnie, gdy nasze wybory bazy danych nie pozwalają na tę „gotową” obsługę replikacji, możemy przejść do sytuacji, w której chcemy wypchnąć „dane podstawowe” do baz danych mikrousług, co może spowodować znaczny wysiłek programisty i być również znacznie mniej wydajnym mechanizmem.

może chcieć denormalizować bazę danych, ale nie możesz tego zrobić, ponieważ dotyczy to wszystkich innych składników

Dla mnie to stwierdzenie jest po prostu nieprawidłowe. Denormalizacja jest zmianą „addytywną”, a nie „przełomową” i żadna aplikacja nie powinna ulec awarii z powodu denormalizacji.

Jedynym sposobem, w jaki ta przerwa aplikacji jest, gdy kod aplikacji używa czegoś takiego jak „wybierz * ...” i nie obsługuje dodatkowej kolumny. Dla mnie to byłby błąd w aplikacji?

Jak denormalizacja może uszkodzić aplikację? Dla mnie to brzmi jak FUD.

Zależność od schematu:

Tak, aplikacja jest teraz zależna od schematu bazy danych i implikuje to, że powinien to być poważny problem. Chociaż dodanie jakiejkolwiek dodatkowej zależności jest oczywiście nie idealne, moim doświadczeniem jest to, że zależność od schematu bazy danych nie była problemem, więc dlaczego tak może być? Czy właśnie miałem szczęście?

Dane podstawowe

Schemat, do którego zazwyczaj możemy chcieć, aby mikrousługa miała dostęp tylko do odczytu, jest najczęściej określany jako „ dane podstawowe ” dla przedsiębiorstwa. Ma podstawowe dane, które są niezbędne dla przedsiębiorstwa.

Historycznie oznacza to, że schemat, od którego dodajemy zależność, jest zarówno dojrzały, jak i stabilny (nieco fundamentalny dla przedsiębiorstwa i niezmienny).

Normalizacja

Jeśli 3 projektantów baz danych pójdzie i zaprojektuje znormalizowany schemat db, otrzymają ten sam projekt. Ok, może być jakieś odmiany 4NF / 5NF, ale niewiele. Co więcej, istnieje szereg pytań, które projektant może zadać w celu walidacji modelu, aby projektant mógł mieć pewność, że dotarł do 4NF (czy jestem zbyt optymistyczny? Czy ludzie mają trudności z dostaniem się do 4NF?).

aktualizacja: Przez 4NF tutaj mam na myśli, że wszystkie tabele w schemacie osiągnęły najwyższą normalną formę do 4NF (wszystkie tabele zostały odpowiednio znormalizowane do 4NF).

Wierzę, że proces projektowania normalizacji jest powodem, dla którego projektanci baz danych są ogólnie zadowoleni z idei polegania na znormalizowanym schemacie bazy danych.

Proces normalizacji przenosi projekt DB do znanego „poprawnego” projektu, a zmiany z niego powinny być denormalizacją pod względem wydajności.

  1. Mogą istnieć odmiany zależne od typów DB (JSON, ARRAY, obsługa typów Geo itp.)
  2. Niektórzy mogą argumentować za zmiennością opartą na 4NF / 5NF
  3. Wykluczamy zmienność fizyczną (ponieważ to nie ma znaczenia)
  4. Ograniczamy to do projektu OLTP, a nie projektu DW, ponieważ są to schematy, do których chcemy przyznać dostęp tylko do odczytu

Gdyby 3 programistów dostało projekt do wdrożenia (jako kod), oczekiwane byłyby 3 różne implementacje (potencjalnie bardzo różne).

Dla mnie potencjalnie jest kwestia „wiary w normalizację”.

Łamanie zmian schematu?

Denormalizacja, dodawanie kolumn, zmiana kolumn w celu zwiększenia przestrzeni dyskowej, rozszerzenie projektu o nowe tabele itp. To niezłomne zmiany, a projektanci DB, którzy osiągnęli normalną 4 klasę, będą tego pewni.

Przerwanie zmian jest oczywiście możliwe przez upuszczenie kolumn / tabel lub zmianę typu podziału. Możliwe, że tak, ale w praktyce nie spotkałem tu żadnych problemów. Być może dlatego, że zrozumiano, jakie są przełomowe zmiany i że zostały one dobrze zarządzane?

Chciałbym usłyszeć przypadki łamania zmian schematu w kontekście wspólnych schematów tylko do odczytu.

Co jest ważniejsze i ważniejsze w mikroserwisie: jego interfejs API lub schemat bazy danych? API, ponieważ taka jest jego umowa z resztą świata.

Chociaż zgadzam się z tym stwierdzeniem, myślę, że istnieje ważne zastrzeżenie, które moglibyśmy usłyszeć od architekta korporacyjnego: „Dane żyją wiecznie” . Oznacza to, że chociaż API może być najważniejszą rzeczą, dane są również dość ważne dla całego przedsiębiorstwa i będą ważne przez bardzo długi czas.

Na przykład, gdy istnieje potrzeba zapełnienia hurtowni danych dla analizy biznesowej, wówczas schemat i obsługa CDC stają się ważne z perspektywy raportowania biznesowego, niezależnie od interfejsu API.

Masz problemy z interfejsami API?

Teraz, gdyby interfejsy API były idealne i łatwe, wszystkie punkty są dyskusyjne, ponieważ zawsze wybieramy interfejs API, a nie lokalny dostęp tylko do odczytu. Motywacją nawet do rozważenia lokalnego dostępu tylko do odczytu jest to, że mogą wystąpić pewne problemy z używaniem interfejsów API, których unika dostęp lokalny.

What motivates people to desire local read-only access?

Optymalizacja API:

LinkedIn ma ciekawą prezentację (od 2009 r.) Na temat optymalizacji API i dlaczego jest dla nich ważna na ich skalę. http://www.slideshare.net/linkedin/building-consistent-restful-apis-in-a-highperformance-environment

Krótko mówiąc, gdy interfejs API musi obsługiwać wiele różnych przypadków użycia, może łatwo przejść do sytuacji, w której obsługuje jeden przypadek użycia optymalnie, a pozostałe dość słabo z perspektywy sieci i bazy danych.

Jeśli interfejs API nie ma takiego samego wyrafinowania jak LinkedIn, możesz łatwo uzyskać scenariusze, w których:

  • Interfejs API pobiera znacznie więcej danych niż potrzebujesz (marnotrawstwo)
  • Chatty API, w których musisz wywoływać API wiele razy

Tak, możemy oczywiście dodać buforowanie do API, ale ostatecznie wywołanie API jest wywołaniem zdalnym i istnieje szereg optymalizacji dostępnych dla programistów, gdy dane są lokalne.

Podejrzewam, że istnieje grupa ludzi, którzy mogliby to dodać:

  • Niska cena replikacji danych podstawowych do bazy danych mikrousług (bez kosztów prac rozwojowych i sprawności technicznej)
  • Wiara w normalizację i odporność aplikacji na zmiany schematu
  • Możliwość łatwej optymalizacji każdego przypadku użycia i potencjalnie unikania rozmownych / marnotrawnych / nieefektywnych zdalnych połączeń API
  • Plus inne korzyści w zakresie ograniczeń i spójnego projektowania

Ta odpowiedź jest zdecydowanie za długa. Przeprosiny!!


Dodanie kolumny zwykle powoduje uszkodzenie aplikacji. Jeśli masz „pensję”, aplikacja sumująca wszystkie pensje zostaje zerwana, gdy zostanie wprowadzona nowa kolumna „pensja_waluta”.
kubańczyk

Naprawdę? Zależy od twojej definicji „przerw”. Jeśli aplikacja była produkowana i działała zgodnie z oczekiwaniami bez „waluty pensji”, dlaczego uważasz, że ta aplikacja jest teraz zepsuta?
Rob Bygrave,

Aplikacja działa bez błędów i wyświetla pewną liczbę. Ale to bezużyteczne. Kiedy dyrektor generalny zobaczy, że suma wynagrodzeń za ostatni miesiąc wynosi 6 milionów zamiast 50 tysięcy (z powodu jednego nowego pracownika, który jest wypłacany w południowokoreańskich Wons), definicja użytecznej / bezużytecznej produkcji nie będzie wiele dyskutowana.
kubańczyk

0

Zarządzanie stanem (potencjalnie bazą danych) można wdrożyć w kontenerze Microservice i udostępnić za pośrednictwem interfejsu API. Baza danych Microservice nie jest widoczna dla innych systemów poza kontenerem - tylko API. Alternatywnie możesz mieć inną usługę (np. Pamięć podręczną) zarządzać stanem za pośrednictwem interfejsu API. Kluczową różnicą w architekturze jest posiadanie wszystkich zależności Microservice (innych niż wywołania API do innych usług) w jednym przenośnym kontenerze. Jeśli tego nie dostaniesz, wróć i przestudiuj architekturę.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.