Witam,
Chciałbym opisać często poruszany problem jakim jest “porównanie szybkości zapytań”… a dokładniej porównaniem wydajności różnych zapytań robiących coś podobnego na takich samych danych w danym środowisku. Z pytaniem o to zagadnienie spotykam się w swojej pracy średnio raz na kilka miesięcy (sporo biorąc pod uwagę ilość osób z którymi współpracuję). Zazwyczaj zaczyna się od wypowiedzi “uruchamiam raz i jest wolno a za drugim razem jest bardzo szybko”.
Szybka odpowiedź na końcu – lecz zachęcam do przeczytania całości wpisu.
Baza danych Oracle w wersji 10 i 11 ocenia zapytania poprzez wyliczenie kosztu zapytania. W ten sposób “zgaduje” jak najlepiej odczytać dane. Więcej informacji można wyszukać pod hasłem “cost based optimizer”. Dla kilku różnych zapytań możemy wygenerować ich plan wykonania i sprawdzić ich koszt celem porównania. Uwaga: koszt jest silnie powiązany z maszyną i zależy m.in. od sprzętu jakim dysponujemy.
Problem: koszt zapytania często nie odzwierciedla prawdziwych danych.
Niepożądane efekty:
- Baza danych wykonuje zapytania w nieoptymalny (wolniejszy niż mogłaby) sposób
- Porównanie zapytań jest co najmniej trudne
- W niektórych sytuacjach koszt zapytania jest wielkością mało wiarygodną (tańszy nie znaczy lepszy)
- Pewnie więcej problemów, z którymi nie miałem jeszcze do czynienia – jeśli znasz takowe to dopisz w komentarzu 😀
Rozwiązanie uniwersalne nie istnieje ponieważ baza jest zbiorem wielu zasobów (pamięć, dysk, CPU, …).
Pierwsze wykonanie zapytania to zazwyczaj odczyt danych z dysku. Drugie – z pamięci cache. Kolejne też z pamięci cache, lecz innej.
Pierwsze uruchomienie oznacza kompilację zapytania (coś, co może trwać długo). Każde następne może być wykonane bez kompilacji. Więcej informacji pod “Oracle CURSOR_SHARING” u ostrzeżeniem: nie włączaj opcji “force” ponieważ ma ona paskudny wpływ na całą bazę…. chyba że dobrze wiesz co robisz.
Rozwiązanie niezbyt dobre aczkolwiek często wykorzystywane to wyczyszczenie pamięci cache: flush cache… a fe.
Lepsza opcja: uruchamiamy aplikację sqlplus, łączymy się do bazy i wykonujemy polecenie “set autotrace only” a następnie wykonujemy nasze zapytanie. Nie zostaną zwrócona żadne wyniki ale ujrzymy statystyki i magiczny atrybut “consistent gets”. Ta wielkość określa ile danych jest odczytanych (z dysku bądź cache) w celu odczytania danych. Idealnie pasuje do porównywania zapytań ograniczonych przez prędkość dysku.
Inne aplikacje oferują tę opcję zazwyczaj jako “autotrace”.
Dodajmy do tego jeszcze “db block gets” (dziękuję Przemku).
Tutaj pojawia się niestety kolejny problem: co jeśli nasze zapytanie nie jest ograniczone przez dysk? Zazwyczaj oznacza to tyle, że należy przemyśleć to, co robimy – nasze zapytanie może stać się strasznie wolne w przyszłości gdy baza się rozrośnie i nie będzie można już polepszyć sprzętu.
Oczywiście po wykonaniu porównania należy sprawdzić skąd biorą się takie a nie inne wartości i czy możemy je jakoś poprawić (np. przez stworzenie indexu, zebranie statystyk, itp.).
Dalej tematu nie będę drążyć… jest on szalenie szeroki i wystarczy chyba określić rozwiązanie jako:
W większości przypadków chcesz uruchomić zapytania w trybie “autotrace” (np. przez sqlplus z “set autotrace only”) i porównać sumę wartości “consistents gets” oraz “db block gets” między zapytaniami.