U jednego z moich pracodawców pracowaliśmy nad interfejsem API REST (ale dotyczy to również SOAP). Klient, który jest interfejsem aplikacji, nawiązywałby połączenia przez Internet (sieć LAN w typowych wdrożeniach produkcyjnych) z interfejsem API. Interfejs API nawiązywałby połączenia z bazą danych.
Jednym z tematów, który powraca w naszych dyskusjach, jest wydajność: niektóre osoby w zespole uważają, że nie powinieneś mieć wielu wywołań bazy danych (zwykle czyta) z jednego wywołania API ze względu na wydajność; powinieneś je zoptymalizować, aby każde wywołanie API miało tylko (dokładnie) jedno wywołanie bazy danych.
Ale czy to naprawdę ważne? Weź pod uwagę, że interfejs użytkownika musi wykonać połączenie sieciowe z interfejsem API; to dość duże (rząd milisekund). Bazy danych są zoptymalizowane do przechowywania rzeczy w pamięci i wykonywania odczytów bardzo, bardzo szybko (np. SQL Server ładuje i przechowuje wszystko w pamięci RAM i zużywa prawie całą wolną pamięć RAM, jeśli to możliwe).
TLDR: Czy naprawdę ważne jest martwienie się wieloma połączeniami z bazą danych, gdy już wykonujemy połączenie sieciowe przez sieć LAN? Jeśli tak, to dlaczego?
Mówiąc wprost, mówię o rzędzie wielkości - wiem, że zależy to od specyfiki (sprzęt maszyny, wybór API i DB itp.) Jeśli mam wywołanie, które zajmuje O (milisekundy), optymalizuje dla DB połączenia, które mają rząd wielkości mniejszy, w rzeczywistości mają znaczenie? Czy może jest coś więcej niż problem?
Edycja: w przypadku potomności uważam, że twierdzenie, że musimy poprawić wydajność, łącząc wywołania bazy danych w takich okolicznościach, jest niedorzeczne - zwłaszcza przy braku profilowania. Jednak to nie moja decyzja, czy to zrobimy, czy nie; Chcę wiedzieć, jakie jest uzasadnienie tego, że jest to prawidłowy sposób optymalizacji wywołań interfejsu API sieci.
źródło
Odpowiedzi:
Logika
Teoretycznie masz rację. Istnieje jednak kilka wad tego uzasadnienia:
Z tego, co powiedziałeś, nie jest jasne, czy faktycznie przetestowałeś / profilowałeś swoją aplikację. Innymi słowy, czy naprawdę wiesz, że transfery sieciowe z aplikacji do interfejsu API są najwolniejszym składnikiem? Ponieważ jest to intuicyjne, łatwo jest założyć, że tak jest. Jednak podczas omawiania wyników nigdy nie należy zakładać. U mojego pracodawcy jestem liderem wydajności. Kiedy po raz pierwszy dołączyłem, ludzie mówili o CDN, replikacji itp. W oparciu o intuicję o tym, jakie wąskie gardła muszą być. Okazuje się, że naszymi największymi problemami z wydajnością były źle działające zapytania do bazy danych.
Mówisz, że ponieważ bazy danych są dobre w pobieraniu danych, baza danych musi działać z najwyższą wydajnością, jest optymalnie używana i nie można nic zrobić, aby ją poprawić. Innymi słowy, bazy danych są zaprojektowane tak, aby były szybkie, więc nigdy nie powinienem się o to martwić. Kolejna niebezpieczna linia myślenia. To tak, jakby powiedzieć, że samochód ma się poruszać szybko, więc nie muszę wymieniać oleju.
Ten sposób myślenia zakłada pojedynczy proces na raz lub, inaczej mówiąc, brak współbieżności. Zakłada, że jedno żądanie nie może wpływać na wydajność innego żądania. Współdzielone zasoby, takie jak dyskowe operacje we / wy, przepustowość sieci, pule połączeń, pamięć, cykle procesora itp. Dlatego też ograniczenie korzystania z jednego zasobu udostępnionego przez jedną bazę danych może zapobiec spowolnieniu innych żądań. Kiedy po raz pierwszy dołączyłem do mojego obecnego pracodawcy, kierownictwo uważało, że dostrojenie 3-sekundowego zapytania do bazy danych było stratą czasu. 3 sekundy to tak mało, po co marnować na to czas? Czy nie byłoby lepiej z CDN, kompresją czy czymś innym? Ale jeśli uda mi się uruchomić 3-sekundowe zapytanie w ciągu 1 sekundy, powiedzmy przez dodanie indeksu, który oznacza 2/3 mniej blokowania, 2/3 mniej czasu spędzonego na wątku, a co ważniejsze, mniej danych odczytywanych z dysku,
Teoria
Istnieje powszechna koncepcja, że wydajność oprogramowania polega po prostu na szybkości .
Z czysto szybkiego punktu widzenia masz rację. System działa tylko tak szybko, jak jego najwolniejszy komponent. Jeśli profilujesz swój kod i odkryłeś, że Internet jest najwolniejszym składnikiem, wtedy wszystko inne oczywiście nie jest najwolniejszą częścią.
Jednak biorąc pod uwagę powyższe, mam nadzieję, że zobaczysz, jak rywalizacja o zasoby, brak indeksowania, źle napisany kod itp. Mogą powodować zaskakujące różnice w wydajności.
Założenia
Ostatnia rzecz. Wspomniałeś, że połączenie z bazą danych powinno być tanie w porównaniu z połączeniem sieciowym z aplikacji do interfejsu API. Ale wspomniałeś również, że aplikacja i serwery API są w tej samej sieci LAN. Czy zatem oba nie są porównywalne z połączeniami sieciowymi? Innymi słowy, dlaczego zakładasz, że transfer API jest o rząd wielkości wolniejszy niż transfer bazy danych, skoro oba mają taką samą dostępną przepustowość? Oczywiście protokoły i struktury danych są różne, rozumiem to, ale kwestionuję założenie, że różnią się one rzędami wielkości.
Gdzie dostaje sierść
Całe to pytanie dotyczy „wielokrotnych” i „pojedynczych” wywołań bazy danych. Ale nie jest jasne, ile jest wielu. Z powodu tego, co powiedziałem powyżej, jako ogólną zasadę zalecam wykonywanie jak najmniejszej liczby wywołań bazy danych, jeśli to konieczne. Ale to tylko praktyczna zasada.
Oto dlaczego:
TL; DR
Tak, ale tylko do pewnego stopnia. Powinieneś spróbować zminimalizować liczbę wywołań bazy danych, gdy jest to praktyczne, ale nie łącz połączeń, które nie mają ze sobą nic wspólnego tylko ze względu na ich połączenie. Unikaj też wywoływania bazy danych w pętli za wszelką cenę.
źródło
Wygląda na to, że Twój zespół optymalizuje się, zanim będzie miał ku temu powód. Czy mierzyłeś czas na wykonanie tych żądań? Szanse na wymuszenie tego paradygmatu spowodują gorszą wydajność dla użytkownika końcowego, ponieważ podróże w obie strony do serwera WWW będą miały znacznie większe opóźnienia niż czas połączenia z serwera WWW do bazy danych. Co więcej, większość przeglądarek nawiąże tylko 2 jednoczesne połączenia z jednym serwerem, więc w przypadku skomplikowanych stron prawdopodobnie napotkasz tam wąskie gardło.
Tak czy inaczej, decyzje dotyczące optymalizacji nie powinny być podejmowane bez danych, które mogłyby je utworzyć. Zmierz to i dowiedz się, co jest najlepsze dla Twojej aplikacji.
źródło
Nie możemy ci powiedzieć.
Nie wiemy, jak wyglądają Twoje zapytania. Nie wiemy, jak długo trzeba czekać. Nie wiemy, jaki jest narzut związany z każdym żądaniem skierowanym do serwera API. Nie wiemy, jak rozproszeni geograficznie są Twoi klienci. Itp.
Jeśli jest to scenariusz, że wymaga optymalizacji i jest jeden, w którym można zdecydować, czy rozłamu lub dołączyć do połączenia ze sobą, trzeba benchmarku to w obie strony : Zdecyduj, co jesteś Optymalizacja (UI latencji, obciążenie procesora serwera, twierdzenie, itp.) i wybierz ten, który lepiej osiąga cel optymalizacji.
Poza tym, tylko jedna rzecz, jaką można dodać ze względną pewnością jest to:
W ramach jednego żądania powinieneś wykonać wszystkie zapytania, które musisz wykonać, aby zbudować odpowiedź.
Innymi słowy, jeśli odpowiedzi nie można wygenerować, dopóki wszystkie N zapytań nie zostaną wykonane, zwykle nie ma sensu ich rozdzielać. Jeśli możesz wygenerować znaczące wyniki, pośrednie lub pełne, po każdym zapytaniu rozpocznij testy porównawcze.
źródło
Dwie myśli:
Po pierwsze, konsument korzystający z interfejsu API wykonuje jedno wywołanie, aby wykonać zadanie. To, co dzieje się po otrzymaniu przez serwer wezwania do wypełnienia żądania, nie powinno być tak sztywne. Jeśli to jedno połączenie od konsumenta wymaga 10 elementów podrzędnych, aby zebrać dane i zwrócić je, powinno to być dopuszczalne.
Po drugie: czy widzisz rzeczywisty problem z wydajnością bazy danych w danym procesie? Z mojego doświadczenia wynika, że często próba umieszczenia wszystkich aspektów żądania bazy danych w jednym wywołaniu może skutkować mniej wydajnym połączeniem niż zwykłe wykonanie trzech lub czterech połączeń danych. Nowoczesne bazy danych są bardzo wydajne w buforowaniu i planach wykonania. Często, gdy próbujesz zrobić zbyt wiele, zobaczysz procedury z kursorami (bardzo niekorzystne dla wydajności, ponieważ dane są odczytywane wiersz po rzędzie, a nie jako zestaw od razu) i kod, który skutkuje mniej wydajnym planem, niż gdybyś złamał wywołanie w kilku małych łatwych krokach.
Ze względu na prostą organizację kodu zgadzam się, że każde wywołanie interfejsu API powinno wywołać jedną procedurę składowaną (lub funkcję db), która z kolei jest odpowiedzialna za wypełnienie żądania. Procedura może obejmować więcej niż jeden krok.
źródło
SELECT
.Jeśli baza danych znajduje się na innym serwerze niż usługa REST, każde wywołanie bazy danych spowoduje objazd sieci i może to znacznie obniżyć wydajność:
Kiedyś zauważyłem, że jedno wywołanie usługi internetowej zostało przetłumaczone na około 500 zapytań do bazy danych - nie był to problem, gdy zarówno usługa internetowa, jak i baza danych znajdują się na tym samym komputerze, ale zmieniły się w czas odpowiedzi wynoszący 6-7 sekund, gdy znajdowały się na innym maszyny
Oczywiście 500 objazdów do bazy danych jest dość ekstremalne. Nie jestem pewien, jakie są twoje wymagania dotyczące wydajności, ale jako ogólną zasadę powiedziałbym, że jeśli pozostaniesz poniżej około 10 zapytań do bazy danych na każde wywołanie REST, nie powinieneś odczuwać znacznego spadku wydajności.
źródło
Mamy kilka aplikacji, które są bardzo, bardzo rozmowne. Dla każdego istnieje połączenie z bazą danych. Pojedynczy. Mało. Rzecz. Udostępnianie danych referencyjnych raz za razem to główna część obciążenia systemu. Całość planowania wątków roboczych, uzyskiwania i usuwania blokad, planowania sprawdzania pamięci podręcznej itp. Sumuje się, nawet jeśli nie ma rzeczywistego We / Wy dysku. Rywalizacja jest większa, ponieważ transakcje muszą blokować wiele wywołań DB, więc przepustowość jest znacznie niższa niż mogłaby być. Zespoły te zastanawiają się teraz nad koniecznością zakupu nowych, bardzo drogich serwerów DB.
Tak więc, chociaż większość czasu, który upłynął w bieżącej konfiguracji systemu, zajmowana jest przez wywołania interfejsu API REST, ignorowanie wydajności na poziomie bazy danych przechowuje problemy na przyszłość.
źródło
Przedstawiona ścieżka optymalizacji to po prostu zły sposób patrzenia na rzeczy.
Wywołania API powinny być atomowe. Innymi słowy, powinienem być w stanie wykonać 1 wywołanie interfejsu API sieci Web, aby wykonać żądane działanie. Niezależnie od tego, czy chodzi o pobieranie danych, aktualizację rekordu czy cokolwiek innego. NIGDY nie należy odbierać więcej niż 1 połączenia, aby spowodować akcję. A próba wykorzystania transakcji w ramach wielu połączeń powinna być odrzucona jak plaga.
Czasami pojedyncze działanie jest raczej złożone. Na przykład pobieranie danych, które są łączone z kilku źródeł: ponownie, powinno to być pojedyncze połączenie. Albo wszystko działa, albo wszystko zawodzi.
Mówienie, że pojedyncze wywołanie interfejsu API powinno wykonać tylko jedno zapytanie DB, jest nieco kretyńskie. Jak już zauważyłeś, narzut związany z zestawieniem połączenia w sieci jest często o rząd wielkości droższy pod względem całkowitego czasu.
Rozumiem nieco ich stwierdzenie, że uruchomienie jednego zapytania może być szybsze niż kilka; ale daje to fałszywe wrażenie, ponieważ ignoruje całkowite obciążenie bazy i obciążenie sieci. Tylko poprzez profilowanie różnych sposobów wyciągania danych z bazy danych możesz dowiedzieć się, na czym polega problem. Jestem pewien, że każdy ma historię, w której określone zapytanie wykonywane 100 razy częściej niż oczekiwano zabijało system, dopóki nie wprowadzono właściwego indeksu ...
Ostatecznie nie będziesz w stanie ich przekonać tylko rozmową. Skonfiguruj przypadek testowy dla obu podejść i profiluj je. Zwróć uwagę na całkowity czas potrzebny na pozyskanie potrzebnych danych, ilość generowanego ruchu sieciowego, liczbę i terminy wywołań bazy danych itp. Podejdź całościowo - co oznacza, że patrzysz na cały system - i powinieneś mieć dużo dane do jedzenia wrony lub pokazywania im złotej ścieżki.
źródło