Czy poszczególne zapytania są szybsze niż dołączenia?

44

Pytanie koncepcyjne: Czy poszczególne zapytania są szybsze niż złączenia, czy: Czy powinienem próbować wycisnąć wszystkie informacje, które chcę po stronie klienta, w jedną instrukcję SELECT, czy po prostu użyć tyle, ile wydaje się wygodne?

TL; DR : Jeśli moje połączone zapytanie trwa dłużej niż uruchamianie pojedynczych zapytań, czy to moja wina, czy należy się tego spodziewać?

Po pierwsze, nie jestem zbyt obeznany z bazami danych, więc to może być tylko ja, ale zauważyłem, że kiedy muszę uzyskać informacje z wielu tabel, „często” szybciej jest uzyskać te informacje poprzez wiele zapytań w poszczególnych tabelach (może zawierające proste sprzężenie wewnętrzne) i załataj dane po stronie klienta, aby spróbować napisać (złożone) połączone zapytanie, w którym mogę uzyskać wszystkie dane w jednym zapytaniu.

Próbowałem połączyć jeden niezwykle prosty przykład:

SQL Fiddle

Konfiguracja schematu :

CREATE TABLE MASTER 
( ID INT NOT NULL
, NAME VARCHAR2(42 CHAR) NOT NULL
, CONSTRAINT PK_MASTER PRIMARY KEY (ID)
);

CREATE TABLE DATA
( ID INT NOT NULL
, MASTER_ID INT NOT NULL
, VALUE NUMBER
, CONSTRAINT PK_DATA PRIMARY KEY (ID)
, CONSTRAINT FK_DATA_MASTER FOREIGN KEY (MASTER_ID) REFERENCES MASTER (ID)
);

INSERT INTO MASTER values (1, 'One');
INSERT INTO MASTER values (2, 'Two');
INSERT INTO MASTER values (3, 'Three');

CREATE SEQUENCE SEQ_DATA_ID;

INSERT INTO DATA values (SEQ_DATA_ID.NEXTVAL, 1, 1.3);
INSERT INTO DATA values (SEQ_DATA_ID.NEXTVAL, 1, 1.5);
INSERT INTO DATA values (SEQ_DATA_ID.NEXTVAL, 1, 1.7);
INSERT INTO DATA values (SEQ_DATA_ID.NEXTVAL, 2, 2.3);
INSERT INTO DATA values (SEQ_DATA_ID.NEXTVAL, 3, 3.14);
INSERT INTO DATA values (SEQ_DATA_ID.NEXTVAL, 3, 3.7);

Zapytanie A :

select NAME from MASTER
where ID = 1

Wyniki :

| NAME |
--------
|  One |

Zapytanie B :

select ID, VALUE from DATA
where MASTER_ID = 1

Wyniki :

| ID | VALUE |
--------------
|  1 |   1.3 |
|  2 |   1.5 |
|  3 |   1.7 |

Zapytanie C :

select M.NAME, D.ID, D.VALUE 
from MASTER M INNER JOIN DATA D ON M.ID=D.MASTER_ID
where M.ID = 1

Wyniki :

| NAME | ID | VALUE |
---------------------
|  One |  1 |   1.3 |
|  One |  2 |   1.5 |
|  One |  3 |   1.7 |

Oczywiście nie mierzyłem z nimi żadnych wyników, ale można zauważyć:

  • Zapytanie A + B zwraca tę samą ilość użytecznych informacji, co zapytanie C.
  • A + B musi zwrócić klientowi 1 + 2x3 == 7 „komórek danych”
  • C musi zwrócić klientowi 3x3 == 9 „komórek danych”, ponieważ w przypadku łączenia dołączam oczywiście pewną nadmiarowość w zestawie wyników.

Uogólniając na podstawie tego (tak dalece jak to jest pobierane):

Połączone zapytanie zawsze musi zwracać więcej danych niż pojedyncze zapytania, które otrzymują tę samą ilość informacji. Ponieważ baza danych musi łączyć dane, w przypadku dużych zestawów danych można założyć, że baza danych musi wykonać więcej pracy na jednym połączonym zapytaniu niż na pojedynczych połączonych zapytaniach, ponieważ (przynajmniej) musi zwrócić więcej danych do klienta.

Czy wynikałoby to z tego, że kiedy zauważę, że podział zapytania po stronie klienta na wiele zapytań daje lepszą wydajność, jest to po prostu droga, czy raczej oznacza to, że pomieszałem połączone zapytanie?

Jaskółka oknówka
źródło
Komentarze nie są przeznaczone do rozszerzonej dyskusji; ta rozmowa została przeniesiona do czatu .
Jack Douglas,
1
Przeprowadziłem test porównawczy i opublikowałem wyniki w artykule na temat medium . Dodałbym tutaj odpowiedź, ale zrobiłbym to już na inne pytanie , a publikowanie tej samej odpowiedzi na wiele pytań jest niezadowolone .
Benjamin

Odpowiedzi:

45

Czy poszczególne zapytania są szybsze niż złączenia, czy: Czy powinienem starać się wycisnąć wszystkie potrzebne informacje po stronie klienta w jedną instrukcję SELECT, czy po prostu użyć tyle, ile wydaje się to wygodne?

W każdym scenariuszu dotyczącym wydajności należy przetestować i zmierzyć rozwiązania, aby sprawdzić, która z nich jest szybsza .

To powiedziawszy, prawie zawsze jest tak, że połączony zestaw wyników z właściwie dostrojonej bazy danych będzie szybszy i skalowany lepiej niż zwracanie wierszy źródłowych do klienta, a następnie dołączanie do nich. W szczególności, jeśli zestawy danych wejściowych są duże, a zestaw wyników jest niewielki - pomyśl o następującym zapytaniu w kontekście obu strategii: połącz ze sobą dwie tabele o wielkości 5 GB każda, z zestawem wyników 100 wierszy. To ekstremalne, ale rozumiesz mój punkt widzenia.

Zauważyłem, że kiedy muszę uzyskać informacje z wielu tabel, „często” szybciej jest uzyskać te informacje za pomocą wielu zapytań w poszczególnych tabelach (być może zawierających proste sprzężenie wewnętrzne) i połączyć dane po stronie klienta, aby spróbować napisać (złożone) połączone zapytanie, w którym mogę uzyskać wszystkie dane w jednym zapytaniu.

Jest wysoce prawdopodobne, że schemat bazy danych lub indeksy mogą zostać ulepszone, aby lepiej obsługiwały zadawane przez Ciebie zapytania.

Połączone zapytanie zawsze musi zwracać więcej danych niż pojedyncze zapytania, które otrzymują tę samą ilość informacji.

Zwykle tak nie jest. Przez większość czasu, nawet jeśli zestawy danych wejściowych są duże, zestaw wyników będzie znacznie mniejszy niż suma danych wejściowych.

W zależności od aplikacji, bardzo duże zestawy wyników zapytania zwracane do klienta są natychmiastową czerwoną flagą: co robi klient z tak dużym zestawem danych, którego nie można zrobić bliżej bazy danych? Wyświetlanie milionom wierszy użytkownikowi jest co najmniej wysoce podejrzane. Przepustowość sieci jest również ograniczonym zasobem.

Ponieważ baza danych musi łączyć dane, w przypadku dużych zestawów danych można założyć, że baza danych musi wykonać więcej pracy na jednym połączonym zapytaniu niż na pojedynczych połączonych zapytaniach, ponieważ (przynajmniej) musi zwrócić więcej danych do klienta.

Niekoniecznie. Jeśli dane są poprawnie zindeksowane, operacja łączenia jest bardziej prawdopodobne w bazie danych bez konieczności skanowania dużej ilości danych. Ponadto silniki relacyjnych baz danych są specjalnie zoptymalizowane na niskim poziomie do łączenia ; stosy klientów nie są.

Czy wynikałoby to z tego, że kiedy zauważę, że podział zapytania po stronie klienta na wiele zapytań daje lepszą wydajność, jest to po prostu droga, czy raczej oznacza to, że pomieszałem połączone zapytanie?

Ponieważ powiedziałeś, że nie masz doświadczenia w zakresie baz danych, proponuję dowiedzieć się więcej o projektowaniu baz danych i dostosowywaniu wydajności. Jestem pewien, że na tym polega problem. Możliwe są również nieefektywnie napisane zapytania SQL, ale z prostym schematem, który jest mniej prawdopodobny.

To nie znaczy, że nie ma innych sposobów na poprawę wydajności. Istnieją scenariusze, w których możesz przeskanować zestaw danych od średnich do dużych i zwrócić je klientowi, jeśli zamierzasz użyć jakiegoś mechanizmu buforowania. Buforowanie może być świetne, ale wprowadza złożoność projektu. Buforowanie może nawet nie być odpowiednie dla Twojej aplikacji.

Jedną z rzeczy, o której nigdzie nie wspomniano, jest zachowanie spójności danych zwracanych z bazy danych. Jeśli używane są oddzielne zapytania, bardziej prawdopodobne (z powodu wielu czynników) są niespójne dane, chyba że dla każdego zestawu zapytań stosowana jest forma izolacji migawki.

Jon Seigel
źródło
+1 dla przepustowości sieci jest również ograniczonym zasobem.
Hari Harker
OP twierdzi, że zestawy wyników danych DOŁĄCZONY są zawsze większe. > Połączone zapytanie zawsze musi zwrócić więcej danych niż poszczególne zapytania. Myślę, że jest to obiektywnie prawdziwe (dla> =), np. Zestawy wyników różnią się rozmiarem, więc więcej danych w sieci. Czy masz przykład, w którym to nie jest prawda? Jeśli dołączę do Autorzy -> Posty i autorzy mają pole o nazwie „biografia”, które jest polem JSON o wielkości 1 MB, dla autora 100 postów, za pośrednictwem drutu prześlę 100 MB vs. 1 MB. Czy to źle?
hytromo
6

Oczywiście nie mierzyłem z nimi żadnej wydajności

Złożyłeś dobry przykładowy kod. Czy spojrzałeś na czas w SQL Fiddle? Nawet niektóre krótkie nienaukowe testy wydajności pokażą, że zapytanie trzy w demonstracji zajmuje tyle samo czasu, co uruchomienie jednego lub dwóch osobno. Połączenie jednego i dwóch zajmuje około dwa razy więcej niż trzy, a więc zanim zostanie wykonane jakiekolwiek połączenie po stronie klienta.

Wraz ze wzrostem danych szybkość pierwszego i drugiego zapytania będzie się różnić, ale przyłączenie do bazy danych będzie nadal szybsze.

Powinieneś również rozważyć, co by się stało, gdyby połączenie wewnętrzne eliminowało dane.

Leigh Riffel
źródło
2

Należy również rozważyć optymalizator zapytań. Jego rolą jest wzięcie deklaratywnego SQL i przetłumaczenie go na kroki proceduralne. Aby znaleźć najbardziej efektywną kombinację kroków proceduralnych, zbada kombinacje użycia indeksu, sortowania, buforowania zestawów wyników pośrednich i wszelkiego rodzaju innych rzeczy. Liczba permutacji może stać się bardzo duża, nawet przy czymś, co wygląda na dość proste zapytania.

Wiele obliczeń wykonanych w celu znalezienia najlepszego planu opiera się na rozkładzie danych w tabelach. Te rozkłady są próbkowane i przechowywane jako obiekty statystyczne. Jeśli są one błędne, prowadzą optymalizatora do dokonywania złych wyborów. Złe wybory na wczesnym etapie planu prowadzą do jeszcze gorszych wyborów później w efekcie śnieżki.

Nie jest nieznane, że zapytanie średniej wielkości zwracające niewielkie ilości danych zajmuje kilka minut. Prawidłowe indeksowanie i dobre statystyki zmniejszają to do milisekund.

Michael Green
źródło
-3

Wiele zapytań JEST właściwą drogą. Jeśli poradzisz sobie z takimi prostymi scenariuszami - narzut kosztów optymalizatora zapytań jest czynnikiem. Przy większej ilości danych pojawia się nieefektywność sieci sprzężenia (redundantne rzędy). Wydajność zapewnia tylko dużo więcej danych.

Na koniec to, czego doświadczasz, jest czymś, co widzi wielu programistów. DBA zawsze mówią „nie, łączenie”, ale rzeczywistość jest taka: w tym przypadku szybsze jest dokonywanie wielu prostych selekcji.

TomTom
źródło
5
W sprzężeniu nie ma „nieefektywności sieci” - wszystko dzieje się na serwerze bazy danych, więc sieć nie jest zaangażowana (chyba że łączysz się przez łącze db!)
Chris Saxon
2
Możesz rozważyć, czy warstwa sieciowa ma kompresję, czy nie. SQL * Net firmy Oracle robi, ponieważ wartości powtarzające się w tej samej kolumnie są skutecznie kompresowane.
David Aldridge
3
@TomTom możesz mieć rację lub nie (jak wskazuje David Aldridge, kompresja ma znaczenie), ale twoje sformułowania są mylące. „nieefektywność sieci połączenia” ? Napraw to, aby było oczywiste, co masz na myśli.
ypercubeᵀᴹ
@ChrisSaxon na pewno jest, obraz zawiera tabele dla raportu „tytuł-> baza-> tabele-wiersze” i potrzebujesz wszystkich wierszy, abyś mógł wewnętrznie połączyć te 3 tabele. Każda tabela ma długie varchary, więc to, co się dzieje, dotyczy każdego wiersza, który powtarzasz. Warstwa aplikacji musi przydzielić pamięć dla wszystkich tych ciągów, a następnie pogrupować je dla swojego modelu. Myślę więc, że o to mu chodzi, wysłano więcej danych
MIKE
@MIKE zależy od wybranych wyrażeń, a nie od sprzężenia. I może istnieć kompresja sieci. W bazie danych Oracle SQL * Net usuwa powtarzające się zduplikowane wartości nicetheory.io/2018/01/11/…
Chris Saxon