Co jest szybsze, jedno duże zapytanie lub wiele małych zapytań?

68

Pracowałem dla różnych firm i zauważyłem, że niektóre z nich wolą mieć poglądy, które dołączą do stołu ze wszystkimi „krewnymi”. Ale w przypadku aplikacji czasami musimy użyć tylko 1 kolumny.

Czy byłoby więc szybsze dokonanie prostych wyborów, a następnie „dołączenie” ich do kodu systemowego?

Systemem może być php, java, asp, dowolny język łączący się z bazą danych.

Pytanie brzmi: co szybciej przechodzi ze strony serwera (php, java, asp, ruby, python ...) do bazy danych, uruchom jedno zapytanie, które dostanie wszystko, czego potrzebujemy, lub przejdź ze strony serwera do bazy danych i uruchom zapytanie, które jednocześnie pobiera kolumny tylko z jednej tabeli?

sudo.ie
źródło
2
Jakiej implementacji „SQL” używasz? MySQL, Microsoft SQL Server, Oracle, Postgresql itp.? Zaktualizuj swój tag.
RLF
1
Mysql i Postgresql
sudo.ie
6
Z mojego doświadczenia wynika, że ​​MySQL nie lubi skomplikowanych zapytań i zwykle jest szybszy z bardzo prostymi zapytaniami (ale więcej). Optymalizator zapytań Postgresa jest znacznie lepszy i tam zwykle wydajniejsze jest uruchamianie pojedynczego dużego zapytania.
a_horse_w_no_name
3
@ a_horse_with_no_name To bardzo szerokie uogólnienie, szczególnie w kontekście tego pytania. Optymalizator MySQL jest z założenia bardzo prosty i może powodować problemy z łączeniami i zapytaniami - szczególnie w starszych wersjach MySQL - które w przeciwnym razie generują szybsze plany w PostgreSQL, podczas gdy MySQL może być bardzo szybki dla czystych ładunków OLTP. Jednak w kontekście pytania jedno duże zapytanie będzie szybsze niż, powiedzmy - w najgorszym możliwym scenariuszu - WYBÓR wewnątrz pętli programowania (bez względu na używany RDBMS).
jynus
2
@jynus: cóż, pytanie jest bardzo szerokie (plus: powiedziałem „z mojego doświadczenia” - inni ludzie mogą mieć inne doświadczenia). Zapytanie wewnątrz pętli nigdy nie jest dobrym pomysłem i prawie zawsze jest wynikiem złego projektu lub braku zrozumienia, jak pracować z relacyjną bazą danych.
a_horse_w_no_name

Odpowiedzi:

68

To, co odpowiedziałoby na twoje pytanie, to temat DOŁĄCZ DO ROZKŁADU.

Według strony 209 książki

Wysoka wydajność MySQL

Możesz dekomponować złączenie, uruchamiając wiele zapytań pojedynczej tabeli zamiast łączenia wielodostępnego, a następnie wykonując połączenie w aplikacji. Na przykład zamiast tego pojedynczego zapytania:

SELECT * FROM tag
JOIN tag_post ON tag_post.tag_id = tag.id
JOIN post ON tag_post.post_id = post.id
WHERE tag.tag = 'mysql';

Możesz uruchomić następujące zapytania:

SELECT * FROM tag WHERE tag = 'mysql';
SELECT * FROM tag_post WHERE tag_id=1234;
SELECT * FROM post WHERE post.id IN (123,456,567,9098,8904);

Dlaczego, u licha, miałbyś to robić? Na pierwszy rzut oka wygląda to na marnotrawstwo, ponieważ zwiększyłeś liczbę zapytań, nie otrzymując nic w zamian. Jednak taka restrukturyzacja może w rzeczywistości dać znaczące korzyści w zakresie wydajności:

  • Buforowanie może być bardziej wydajne. Wiele aplikacji buforuje „obiekty”, które są mapowane bezpośrednio na tabele. W tym przykładzie, jeśli obiekt ze znacznikiem mysqljest już buforowany, aplikacja pominie pierwsze zapytanie. Jeśli znajdziesz w pamięci podręcznej posty o identyfikatorze 123, 567 lub 908, możesz usunąć je z IN()listy. Pamięć podręczna zapytań może również skorzystać z tej strategii. Jeśli tylko jedna z tabel zmienia się często, dekompozycja złączenia może zmniejszyć liczbę unieważnień pamięci podręcznej.
  • Wykonywanie zapytań indywidualnie może czasem zmniejszyć rywalizację o blokadę
  • Wykonanie złączeń w aplikacji ułatwia skalowanie bazy danych poprzez umieszczenie tabel na różnych serwerach.
  • Same zapytania mogą być bardziej wydajne. W tym przykładzie użycie IN()listy zamiast łączenia pozwala MySQL sortować identyfikatory wierszy i pobierać wiersze bardziej optymalnie, niż byłoby to możliwe w przypadku łączenia.
  • Możesz ograniczyć zbędny dostęp do wierszy. Wykonanie sprzężenia w aplikacji oznacza pobranie każdego wiersza tylko raz., Natomiast sprzężenie w zapytaniu jest zasadniczo denormalizacją, która może wielokrotnie uzyskiwać dostęp do tych samych danych. Z tego samego powodu taka restrukturyzacja może również zmniejszyć całkowity ruch sieciowy i zużycie pamięci.
  • Do pewnego stopnia można zobaczyć tę technikę jako ręczne wdrażanie sprzężenia mieszającego zamiast algorytmu zagnieżdżonych pętli, którego MySQL używa do wykonania sprzężenia. Łączenie mieszające może być bardziej wydajne.

W rezultacie sprzężenia czynności w aplikacji mogą być bardziej wydajne, gdy buforujesz i ponownie wykorzystujesz wiele danych z wcześniejszych zapytań, rozprowadzasz dane na wiele serwerów, zamieniasz IN()sprzężenia na listy lub sprzężenie odnosi się wielokrotnie do tej samej tabeli.

OBSERWACJA

Podoba mi się pierwszy punktor, ponieważ InnoDB jest trochę ciężki, gdy sprawdza pamięć podręczną zapytania.

Jeśli chodzi o ostatni punkt, napisałem post 11 marca 2013 r. ( Czy istnieje różnica w wykonywaniu między warunkiem JOIN a warunkiem WHERE? ), Który opisuje algorytm zagnieżdżonej pętli. Po przeczytaniu zobaczysz, jak dobry może być rozkład złączeń.

Podobnie jak w przypadku wszystkich innych punktów z książki , programiści naprawdę szukają wydajności jako dolnej linii. Niektóre polegają na zewnętrznych środkach (poza aplikacją) w celu zwiększenia wydajności, takich jak użycie szybkiego dysku, uzyskanie większej liczby procesorów / rdzeni, dostrojenie silnika pamięci i dostrojenie pliku konfiguracyjnego. Inni zapinają się i piszą lepszy kod. Niektórzy mogą uciekać się do kodowania całej analizy biznesowej w Procedurach składowanych, ale nadal nie stosują dekompozycji łączenia (zobacz Jakie argumenty przemawiają przeciwko lub za umieszczeniem logiki aplikacji w warstwie bazy danych? Wraz z innymi postami). Wszystko zależy od kultury i tolerancji każdego sklepu programisty.

Niektórzy mogą być zadowoleni z wydajności i nie dotykać kodu. Inni po prostu nie zdają sobie sprawy z wielkich korzyści, które można czerpać, jeśli spróbują dołączyć do kompozycji.

Dla programistów, którzy chcą ...

SPRÓBUJ !!!

RolandoMySQLDBA
źródło
3
Jeśli chodzi o link do zmiany na 3 zapytania ... Znam i szanuję Barona, Vadima i Petera, ale nie zgadzam się z tą wprowadzającą w błąd sugestią. Większość argumentów za podziałem jest tak rzadka, że ​​nie warto o nich wspominać. Trzymaj się jednego zapytania za pomocą JOIN, a następnie popracujmy nad jego ulepszeniem.
Rick James
2
@RickJames Zgadzam się z duchem twojego komentarza. Przez lata widziałem, jak jedni pracują przy rozkładzie, a inni nie. Nawet przy odpowiednim zestawie umiejętności SQL może działać przeciwko tobie, jeśli dekompozycja łączenia nie zostanie wykonana poprawnie. U mojego obecnego pracodawcy wiele osób lubi zwiększanie i zmniejszanie skali, szczególnie gdy w grę wchodzi starszy kod i dostępne są głębokie kieszenie. Z tymi, którzy mają smak kawioru, ale budżet na sałatki jajeczne, dołączenie do rozkładu może być warte ryzyka, ale należy to zrobić dobrze.
RolandoMySQLDBA
Chciałbym zobaczyć, jak to działa w środowisku Oracle, gdybym miał prawa i czas.
Rick Henderson
Innym sposobem może być szybsze, że jeśli robisz porządkowanie, ogólnie mniej obliczeń do zamówienia mniejszych list niż do zamówienia jednej dużej listy.
Evan Siroky
24

W Postgres (i prawdopodobnie w dowolnym RDBMS w podobnym stopniu, MySQL w mniejszym stopniu) mniej zapytań jest prawie zawsze znacznie szybszych.

Narzut związany z analizowaniem i planowaniem wielu zapytań jest już większy niż jakikolwiek możliwy zysk w większości przypadków.

Nie mówiąc już o dodatkowej pracy do wykonania u klienta, łącząc wyniki, które zwykle są znacznie wolniejsze. RDBMS specjalizuje się w tego rodzaju zadaniach, a operacje są oparte na oryginalnych typach danych. Brak rzutowania do texti z powrotem dla wyników pośrednich lub przekształcania do rodzimych typów klienta, co może nawet prowadzić do mniej poprawnych (lub niepoprawnych!) Wyników. Pomyśl o liczbach zmiennoprzecinkowych ...

Przesyłasz także więcej danych między serwerem DB a klientem. Może to być nieistotne w przypadku ręki pełnej wartości lub mieć ogromną różnicę.

Jeśli wiele zapytań oznacza wiele podróży w obie strony do serwera bazy danych, zbierasz również wielokrotnie opóźnienie sieci i narzut transakcji, być może nawet narzut połączenia. Wielka, wielka strata.

W zależności od konfiguracji samo opóźnienie sieci może trwać dłużej niż cała reszta o rząd wielkości.

Powiązane pytanie dotyczące SO:

Punktem zwrotnym mogą być bardzo duże , długo działające zapytania, ponieważ po drodze transakcje zbierają blokady wierszy DB. Bardzo duże zapytania mogą zawierać wiele blokad przez dłuższy czas, co może powodować tarcie przy równoczesnych zapytaniach .

Erwin Brandstetter
źródło
Z ciekawości, co uważasz za bardzo duże ?
Sablefoste
@Sablefoste: Bardzo zależy od wzorców dostępu. Punktem krytycznym jest miejsce, w którym równoległe transakcje zaczynają się w kolejce, czekając na zwolnienie blokad lub jeśli zgromadzisz wystarczającą liczbę blokad, aby zjeść znaczną część zasobów. Lub jeśli twoje zapytania trwają wystarczająco długo, aby zakłócać autovacuum ...
Erwin Brandstetter
Ale jeśli weźmiemy nieco typową sytuację - zapytanie, które wykorzystuje sprzężenie zewnętrzne i zwraca wiele zbędnych danych dla tabeli „nadrzędnej”, które następnie musi zostać przeanalizowane i posortowane przez aplikację (najprawdopodobniej część biblioteki ORM) w porównaniu do mały wybór, który najpierw pobiera wszystkie wymagane identyfikatory, a następnie inny mniejszy wybór za pomocą IN () zamiast łączenia zewnętrznego? Czy drugie podejście nie będzie bardziej wydajne (biorąc pod uwagę zarówno zużycie procesora, jak i przepustowość komunikacji przez DB i aplikację)?
JustAMartin
1
@JustAMartin: To brzmi jak zapytanie, które jest prawie na pewno szybsze, gdy jest obsługiwane przez narzędzie do planowania zapytań RDBMS - przy założeniu poprawnych zapytań. Dotyczy returns lots of redundant data for "parent" table: dlaczego zwracasz zbędne dane? Zwracaj tylko potrzebne dane.
Erwin Brandstetter,
1
Z zewnętrznym złączeniem RDBMS zwraca dane z tabeli nadrzędnej zduplikowane dla każdego przyłączonego podrzędnego, co oznacza pewne obciążenie sieci i pamięci, a następnie dodatkowe analizowanie w narzędziu ORM, aby wyrzucić zduplikowane wartości nadrzędne i zachować tylko jednego rodzica z n dziećmi. Dzięki pojedynczemu zapytaniu oszczędzamy na wydajnej pracy narzędzia do planowania zapytań RDBMS, zmniejszeniu liczby żądań sieciowych (lub potoków lokalnych), ale tracimy na niepotrzebnej ładowności i przenoszeniu danych w bibliotece ORM. Wydaje mi się, że tak jak zawsze - zmierz przed optymalizacją.
JustAMartin