Czy wielokrotne wywołania bazy danych są naprawdę znaczące w przypadku połączenia sieciowego API sieci?

16

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.

ashes999
źródło
Czy nie ma innego połączenia sieciowego między warstwą API a bazą danych?
Podpisz
4
Co wykazały twoje testy czasowe?
Dan Pichelman,
@Sign Nie ma połączenia sieciowego między interfejsem API a bazą danych. Z tego, co rozumiem, na pewno są na tej samej maszynie.
ashes999
@DanPichelman o to też proszę. Wydaje się, że nikt nie bierze i nie mierzy wydajności; otrzymujemy tylko wymagania „poprawienia wydajności w X przez połączenie wszystkich wywołań DB w jedno wywołanie”.
ashes999

Odpowiedzi:

25

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).

Logika

Teoretycznie masz rację. Istnieje jednak kilka wad tego uzasadnienia:

  1. 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.

  2. 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.

  3. 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:

  1. Bazy danych doskonale odczytują dane. Są silnikami pamięci. Jednak Twoja logika biznesowa funkcjonuje w Twojej aplikacji. Jeśli ustawisz regułę, że każde wywołanie interfejsu API powoduje dokładnie jedno wywołanie bazy danych, logika biznesowa może skończyć się w bazie danych. Może to jest w porządku. Robi to wiele systemów. Ale niektórzy nie. Chodzi o elastyczność.
  2. Czasami w celu uzyskania dobrego oddzielenia należy oddzielić 2 połączenia z bazą danych. Na przykład być może każde żądanie HTTP jest kierowane przez ogólny filtr bezpieczeństwa, który potwierdza, że ​​użytkownik ma odpowiednie prawa dostępu z bazy danych. Jeśli tak, przejdź do wykonania funkcji odpowiedniej dla tego adresu URL. Ta funkcja może wchodzić w interakcje z bazą danych.
  3. Wywoływanie bazy danych w pętli. Dlatego zapytałem, ile jest wielokrotności. W powyższym przykładzie byłyby 2 wywołania bazy danych. 2 jest w porządku. 3 może być w porządku. N nie jest w porządku. Jeśli wywołujesz bazę danych w pętli, sprawiłeś, że wydajność jest liniowa, co oznacza, że ​​zajmie to więcej czasu, tym bardziej na wejściu pętli. Tak kategorycznie mówiąc, że czas sieci API jest najwolniejszy, całkowicie pomija anomalie, takie jak 1% twojego ruchu, zajmuje dużo czasu z powodu jeszcze nie odkrytej pętli, która wywołuje bazę danych 10.000 razy.
  4. Czasami są rzeczy, w których Twoja aplikacja jest lepsza, na przykład złożone obliczenia. Może być konieczne odczytanie niektórych danych z bazy danych, wykonanie obliczeń, a następnie na podstawie wyników przekazanie parametru do drugiego wywołania bazy danych (być może w celu zapisania wyników). Jeśli połączysz je w jedno wywołanie (np. Procedurę przechowywaną) tylko po to, by wywołać bazę danych tylko raz, zmusisz się do korzystania z bazy danych do czegoś, w czym serwer aplikacji może być lepszy.
  5. Równoważenie obciążenia: Masz 1 bazę danych (przypuszczalnie) i wiele serwerów aplikacji z równoważeniem obciążenia. Dlatego im więcej pracy wykonuje aplikacja i im mniej bazy danych, tym łatwiej jest skalować, ponieważ ogólnie łatwiej jest dodać serwer aplikacji niż konfigurację replikacji bazy danych. Na podstawie poprzedniego punktu wypunktowania może być sensowne uruchomienie zapytania SQL, a następnie wykonanie wszystkich obliczeń w aplikacji, która jest rozproszona na wiele serwerów, a następnie zapisanie wyników po zakończeniu. Może to dać lepszą przepustowość (nawet jeśli całkowity czas transakcji jest taki sam).

TL; DR

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?

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ę.

Brandon
źródło
3

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.

brianfeucht
źródło
1
To dobry komentarz na temat naszych złych praktyk w zakresie wydajności, ale nie odpowiada na moje pytanie, czy połączenia DB są czymś, o co należy się martwić, kiedy już mam połączenie sieciowe.
ashes999
1
Ogólnie stwierdziłem, że wykonywanie wielu wywołań bazy danych nie stanowi problemu. Wynika to głównie z puli połączeń i niewielkich opóźnień między bazą danych a serwerem WWW. Jest pewien moment, w którym wykonywanie różnych wywołań db wpłynie negatywnie na wydajność, ale nie mam dla ciebie twardego numeru. Wszystko zależy od środowiska i aplikacji. Tylko pomiar da odpowiedź, której szukasz.
brianfeucht
Nie powinno to (koniecznie) zależeć od szczegółów, ponieważ mówię o rzędzie wielkości.
ashes999
Po prostu szorstkie domysły (musisz zmierzyć): Średni czas połączenia z serwerem WWW z serwera WWW: 2 ms Średni czas połączenia z serwerem WWW z klienta: 20 ms Tak więc zakładając, że liczby, które losowo wyciągnąłem z sieci są prawidłowe, możesz zrobić 10 połączenia z bazą danych w czasie potrzebnym do wykonania jednego połączenia z usługą internetową. Zakładając, że zapytania do bazy danych zajmują tyle samo czasu. Te liczby są bardzo zależne od środowiska. Jeśli klient wykonujący połączenie z usługą internetową jest lokalny, może to zrobić o kilka rzędów wielkości.
brianfeucht
2

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.

svidgen
źródło
1

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.

Richard
źródło
Zgadzam się z tobą w kwestii mierzenia wydajności, czego nikt chyba nie robi. Nie ma dowodów na to, że jest to szybsze, ale wciąż się zbliża. Wydajność pojawia się jako problem, gdy mamy kilka połączeń, które mogą, powiedzmy, 1000 DB SELECT.
ashes999
@ ashes999, chociaż możesz przyspieszyć patrząc na liczbę wywołań db, bardziej prawdopodobne jest to w strategii indeksowania itp., a nie liczbie wywołań. Jak wszyscy wskazywali, spójrz na dane dotyczące wydajności.
Richard
Richard, zgadzam się i faktycznie to wiem. Moje pytanie brzmi: dlaczego różne osoby ciągle wspominają o tym, że „wiele połączeń DB jest powolnych”, gdy w grę wchodzi połączenie sieciowe. Naprawdę nie rozumiem, jak to może być znaczące.
ashes999
@ ashes999 Przepraszam, może powinieneś bardziej szczegółowo omówić połączenie sieciowe, ponieważ wydaje się to oczywiste, czuję, że w twoim pytaniu jest coś więcej. Czuję, że czegoś brakuje w twoich pytaniach. Zawsze będziesz odczuwać pewne opóźnienia sieciowe, a każde połączenie potencjalnie zwiększa się o „x” razy dla każdego połączenia (krótko mówiąc). Instrukcja po wartości nominalnej jest prawdą, wiele połączeń sieciowych będzie wolniejszych niż jedno połączenie sieciowe do bazy danych. Dlatego proponuję jedno wywołanie procedury składowanej, które może wykonywać wiele wywołań bazy danych bez wielu połączeń sieciowych.
Richard
1

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.

Astrotrain
źródło
1

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ść.

Michael Green
źródło
0

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.

Nie ja
źródło