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:
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?
źródło
Odpowiedzi:
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.
Jest wysoce prawdopodobne, że schemat bazy danych lub indeksy mogą zostać ulepszone, aby lepiej obsługiwały zadawane przez Ciebie zapytania.
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.
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ą.
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.
źródło
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.
źródło
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.
źródło
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.
źródło