Ulubione sztuczki dostrajania wydajności [zamknięte]

126

Jeśli masz zapytanie lub procedurę składowaną, która wymaga dostrojenia wydajności, jakie są niektóre z pierwszych rzeczy, które należy wypróbować?

Terrapin
źródło
Oto kilka sztuczek dotyczących optymalizacji zapytań SQL Server
SQLMenace
Zgadzam się, że to nie jest konstruktywne i da się szukać w Google, ale dlaczego ma 118 uv ?! :)
FLICKER

Odpowiedzi:

114

Oto poręczna lista rzeczy, które zawsze daję komuś, kto pyta mnie o optymalizację.
Używamy głównie Sybase, ale większość porad ma zastosowanie we wszystkich przypadkach.

Na przykład SQL Server jest dostarczany z wieloma bitami monitorowania / dostrajania wydajności, ale jeśli nie masz czegoś takiego (a może nawet jeśli masz), rozważę następujące ...

99% problemów , które widziałem, jest spowodowanych umieszczeniem zbyt wielu tabel w złączeniu . Rozwiązaniem tego problemu jest wykonanie połowy złączenia (z niektórymi tabelami) i buforowanie wyników w tabeli tymczasowej. Następnie wykonaj resztę zapytania, dołączając do tej tabeli tymczasowej.

Lista kontrolna optymalizacji zapytań

  • Uruchom AKTUALIZUJ STATYSTYKI w tabelach źródłowych
    • Wiele systemów wykonuje to jako zaplanowane cotygodniowe zadanie
  • Usuń rekordy z bazowych tabel (ewentualnie zarchiwizuj usunięte rekordy)
    • Rozważ zrobienie tego automatycznie raz dziennie lub raz w tygodniu.
  • Odbuduj indeksy
  • Odbuduj tabele (wyjście / wejście danych bcp)
  • Zrzuć / przeładuj bazę danych (drastyczne, ale może naprawić uszkodzenie)
  • Zbuduj nowy, bardziej odpowiedni indeks
  • Uruchom DBCC, aby sprawdzić, czy istnieje możliwość uszkodzenia bazy danych
  • Zamki / zakleszczenia
    • Upewnij się, że w bazie danych nie działają żadne inne procesy
      • Szczególnie DBCC
    • Czy używasz blokowania na poziomie wiersza lub strony?
    • Zablokuj tabele wyłącznie przed rozpoczęciem zapytania
    • Sprawdź, czy wszystkie procesy uzyskują dostęp do tabel w tej samej kolejności
  • Czy indeksy są używane właściwie?
    • Połączenia będą używać indeksu tylko wtedy, gdy oba wyrażenia są dokładnie tego samego typu danych
    • Indeks będzie używany tylko wtedy, gdy pierwsze pola indeksu są zgodne w zapytaniu
    • Czy w odpowiednich przypadkach stosowane są indeksy klastrowe?
      • dane zakresu
      • WHERE pole między wartością1 i wartością2
  • Małe połączenia to ładne połączenia
    • Domyślnie optymalizator bierze pod uwagę tylko 4 tabele naraz.
    • Oznacza to, że przy połączeniach z więcej niż 4 tabelami ma duże szanse na wybranie nieoptymalnego planu zapytań
  • Przerwij połączenie
    • Czy możesz zerwać połączenie?
    • Wybierz wstępnie klucze obce do tymczasowej tabeli
    • Wykonaj połowę sprzężenia i umieść wyniki w tymczasowej tabeli
  • Czy używasz odpowiedniego rodzaju tabeli tymczasowej?
    • #temptabele mogą działać znacznie lepiej niż @tablezmienne o dużych wolumenach (tysiące wierszy).
  • Utrzymuj tabele podsumowań
    • Twórz z wyzwalaczami w bazowych tabelach
    • Buduj codziennie / co godzinę / itp.
    • Twórz ad-hoc
    • Buduj stopniowo lub niszcz / przebudowuj
  • Zobacz, jaki jest plan kwerend z ustawieniem SHOWPLAN ON
  • Zobacz, co się właściwie dzieje, gdy funkcja SET STATS IO ON
  • Wymuś indeks używając pragmy: (index: myindex)
  • Wymuś kolejność tabel za pomocą SET FORCEPLAN ON
  • Wąchanie parametrów:
    • Podziel procedurę przechowywaną na 2
    • zadzwoń do proc2 z proc1
    • pozwala optymalizatorowi wybrać indeks w proc2, jeśli @parameter został zmieniony przez proc1
  • Czy możesz ulepszyć swój sprzęt?
  • O której biegasz? Czy jest spokojniejszy czas?
  • Czy działa serwer replikacji (lub inny nieprzerwany proces)? Czy możesz to zawiesić? Uruchom go np. cogodzinny?
AJ.
źródło
2
do którego fragmentu się odnosisz?
AJ.
2
To fajne rzeczy, ale chciałbym, żebyś miał jakieś odniesienia do niektórych twierdzeń. Na przykład: Nigdy nie słyszałem, żeby optymalizacja uwzględniała tylko 4 stoły na raz w połączeniu. Nie rozumiem, jak to może być słuszne. Czy mógłbyś w szczególności podać jakieś referencje? Chciałbym zobaczyć, gdzie to dostajesz.
SheldonH
19
  1. Miej całkiem niezłe wyobrażenie o optymalnej ścieżce uruchomienia zapytania w swojej głowie.
  2. Sprawdź plan zapytań - zawsze.
  3. Włącz STATS, aby móc zbadać wydajność we / wy i procesora. Skoncentruj się na obniżaniu tych liczb, niekoniecznie na czasie zapytań (ponieważ może na to wpływać inna aktywność, pamięć podręczna itp.).
  4. Szukaj dużej liczby wierszy przychodzących do operatora, ale wychodzących małych liczb. Zwykle indeks pomógłby, ograniczając liczbę przychodzących wierszy (co zapisuje odczyty dysku).
  5. Skoncentruj się najpierw na największym poddrzewie kosztów. Zmiana tego poddrzewa często może zmienić cały plan kwerend.
  6. Typowe problemy, które widziałem, to:
    • Jeśli jest dużo sprzężeń, czasami Sql Server zdecyduje się rozszerzyć sprzężenia, a następnie zastosuje klauzule WHERE. Zwykle można to naprawić, przenosząc warunki WHERE do klauzuli JOIN lub tabeli pochodnej z warunkami wstawionymi. Widoki mogą powodować te same problemy.
    • Nieoptymalne sprzężenia (LOOP vs HASH vs MERGE). Moją praktyczną zasadą jest użycie sprzężenia LOOP, gdy górny rząd ma bardzo mało wierszy w porównaniu z dolnym, MERGE, gdy zestawy są z grubsza równe i uporządkowane, a HASH dla wszystkiego innego. Dodanie wskazówki dotyczącej łączenia pozwoli Ci przetestować swoją teorię.
    • Wąchanie parametrów. Jeśli najpierw uruchomiłeś przechowywany proces z nierealistycznymi wartościami (powiedzmy do testowania), buforowany plan zapytań może być nieoptymalny dla twoich wartości produkcyjnych. Uruchomienie ponownie Z RECOMPILE powinno to zweryfikować. W przypadku niektórych przechowywanych procesów, szczególnie tych, które zajmują się różnymi rozmiarami zakresów (powiedzmy, wszystkie daty między dniem dzisiejszym a wczoraj - co pociągałoby za sobą WYSZUKIWANIE INDEKSU - lub wszystkie daty między ostatnim a tym rokiem - co byłoby lepsze dzięki SKANOWANIU INDEKSU ) może być konieczne uruchomienie go za każdym razem Z RECOMPILE.
    • Złe wcięcie ... OK, więc Sql Server nie ma z tym problemu - ale z pewnością nie mogę zrozumieć zapytania, dopóki nie naprawię formatowania.
Mark Brackett
źródło
1
+1 za włączenie złego wcięcia. Formatowanie jest kluczowe! :)
mwigdahl
18

Trochę poza tematem, ale jeśli masz kontrolę nad tymi kwestiami ...
Wysoki poziom i duży wpływ.

  • W przypadku środowisk z wysokim IO upewnij się, że dyski są przeznaczone dla macierzy RAID 10 lub RAID 0 + 1 lub jakiejś zagnieżdżonej implementacji raid 1 i raid 0.
  • Nie używaj dysków mniejszych niż 1500 KB.
  • Upewnij się, że dyski są używane tylko dla bazy danych. IE bez logowania bez systemu operacyjnego.
  • Wyłącz automatyczne powiększanie lub podobną funkcję. Pozwól bazie danych wykorzystać całą przewidywaną pamięć. Niekoniecznie to, co jest obecnie używane.
  • zaprojektuj schemat i indeksy dla zapytań typu.
  • jeśli jest to tabela typu dziennika (tylko wstawianie) i musi znajdować się w bazie danych, nie indeksuj jej.
  • jeśli zajmujesz się raportowaniem (złożone selekcje z wieloma połączeniami), powinieneś przyjrzeć się tworzeniu hurtowni danych ze schematem gwiazdy lub płatka śniegu.
  • Nie bój się replikować danych w zamian za wydajność!
jason saldo
źródło
8

CREATE INDEX

Upewnij się, że dostępne są indeksy dla Twoich WHEREi JOINklauzul. To znacznie przyspieszy dostęp do danych.

Jeśli Twoje środowisko to hurtownia danych lub hurtownia danych, indeksy powinny obfitować w prawie wszystkie możliwe zapytania.

W środowisku transakcyjnym liczba indeksów powinna być niższa, a ich definicje bardziej strategiczne, aby utrzymanie indeksów nie obciążało zasobów. (Utrzymanie indeksu ma miejsce, gdy liście indeksu muszą zostać zmienione, aby odzwierciedlić zmianę w tabeli bazowej, tak jak w przypadku operacji INSERT, UPDATE,i DELETE).

Zwróć także uwagę na kolejność pól w indeksie - im bardziej selektywne (większa liczność) pole, tym wcześniej powinno się pojawić w indeksie. Załóżmy na przykład, że szukasz używanych samochodów:

SELECT   i.make, i.model, i.price
FROM     dbo.inventory i
WHERE    i.color = 'red'
  AND    i.price BETWEEN 15000 AND 18000

Cena ma generalnie wyższą moc. Może być dostępnych tylko kilkadziesiąt kolorów, ale prawdopodobnie tysiące różnych cen wywoławczych.

Spośród tych opcji indeksu idx01zapewnia szybszą ścieżkę do spełnienia zapytania:

CREATE INDEX idx01 ON dbo.inventory (price, color)
CREATE INDEX idx02 ON dbo.inventory (color, price)

Dzieje się tak, ponieważ mniej samochodów spełnia wymagania cenowe niż wybór koloru, co daje silnikowi zapytań znacznie mniej danych do analizy.

Wiadomo, że mam dwa bardzo podobne indeksy różniące się tylko kolejnością pól przyspieszających zapytania (imię, nazwisko) w jednym i (nazwisko, imię) w drugim.

Czy SQL dla żywności
źródło
6

Sztuczka, której ostatnio się nauczyłem, polega na tym, że SQL Server może aktualizować zmienne lokalne, a także pola, w instrukcji aktualizacji.

UPDATE table
SET @variable = column = @variable + otherColumn

Lub bardziej czytelna wersja:

UPDATE table
SET
    @variable = @variable + otherColumn,
    column = @variable

Użyłem tego do zastąpienia skomplikowanych kursorów / złączeń podczas wykonywania obliczeń rekurencyjnych, a także znacznie zyskałem na wydajności.

Oto szczegóły i przykładowy kod, który wprowadził fantastyczną poprawę wydajności: http://geekswithblogs.net/Rhames/archive/2008/10/28/calculating-running-totals-in-sql-server-2005---the-optimal. aspx

jandersson
źródło
5

Zakładając tutaj MySQL, użyj EXPLAIN, aby dowiedzieć się, co się dzieje z zapytaniem, upewnij się, że indeksy są używane tak wydajnie, jak to możliwe, i spróbuj wyeliminować sortowanie plików. Wysokowydajny MySQL: optymalizacja, kopie zapasowe, replikacja i nie tylko to świetna książka na ten temat, podobnie jak MySQL Performance Blog .

davidmytton
źródło
3
To dobrze dla MySQL, ale pytanie zostało oznaczone jako „sqlserver”. Mimo to dobrze jest to zrobić. Analogiczną rzeczą do zrobienia w SSMS jest użycie „Wyświetl szacowany plan wykonania” i „Uwzględnij rzeczywisty plan wykonania”. Jeśli możesz wyeliminować ogromne skany tabel i użyć wyszukiwania indeksów klastrowych, jesteś na dobrej drodze do optymalnej wydajności.
eksortso
5

@Terrapin Istnieje kilka innych różnic między isnull i coalesce, o których warto wspomnieć (poza zgodnością z ANSI, która jest dla mnie duża).

Coalesce vs. IsNull

AlexCuse
źródło
3

Czasami w SQL Server, jeśli użyjesz OR w klauzuli Where, naprawdę zmniejszy to wydajność. Zamiast używać OR, po prostu wykonaj dwie selekcje i połącz je razem. Takie same wyniki uzyskuje się przy 1000-krotnej szybkości.

Ryan
źródło
Widziałem to niewyjaśnione zachowanie.
Esen
2

Spójrz na klauzulę where - sprawdź użycie indeksów / sprawdź, czy nic głupiego nie zostało zrobione

where SomeComplicatedFunctionOf(table.Column) = @param --silly
Mikrofon
źródło
2

Generalnie zacznę od złączeń - wyrzucę każde z nich z zapytania pojedynczo i ponownie uruchomię zapytanie, aby dowiedzieć się, czy istnieje konkretne złączenie, z którym mam problem.

John Christensen
źródło
2

Na wszystkich moich tabelach tymczasowych lubię dodawać unikalne ograniczenia (w stosownych przypadkach), aby tworzyć indeksy i klucze podstawowe (prawie zawsze).

declare @temp table(
    RowID int not null identity(1,1) primary key,
    SomeUniqueColumn varchar(25) not null,
    SomeNotUniqueColumn varchar(50) null,
    unique(SomeUniqueColumn)
)
Seibar
źródło
2

Przyzwyczaiłem się zawsze używać zmiennych wiążących. Możliwe, że zmienne powiązania nie pomogą, jeśli RDBMS nie buforuje instrukcji SQL. Ale jeśli nie używasz zmiennych wiążących, RDBMS nie ma szansy na ponowne wykorzystanie planów wykonywania zapytań i przeanalizowanych instrukcji SQL. Oszczędności mogą być ogromne: http://www.akadia.com/services/ora_bind_variables.html . Pracuję głównie z Oracle, ale Microsoft SQL Server działa prawie w ten sam sposób.

Z mojego doświadczenia wynika, że ​​jeśli nie wiesz, czy używasz zmiennych wiążących, prawdopodobnie tak nie jest. Jeśli Twój język aplikacji ich nie obsługuje, znajdź taki, który to obsługuje. Czasami można naprawić zapytanie A, używając zmiennych wiązania dla zapytania B.

Następnie rozmawiam z naszym DBA, aby dowiedzieć się, co powoduje największy ból w RDBMS. Zauważ, że nie powinieneś pytać „Dlaczego to zapytanie jest wolne?” To tak, jakby poprosić lekarza, aby usunął wyrostek robaczkowy. Pewnie, że Twoje zapytanie może być problemem, ale jest równie prawdopodobne, że coś innego jest nie tak. Jako programiści myślimy raczej w kategoriach linii kodu. Jeśli linia jest wolna, napraw ją. Ale RDBMS to naprawdę skomplikowany system i twoje powolne zapytanie może być symptomem znacznie większego problemu.

Zbyt wiele wskazówek dotyczących tuningu SQL to idole kultu cargo. W większości przypadków problem jest niezwiązany lub minimalnie związany z używaną składnią, więc zwykle najlepiej jest używać możliwie najczystszej składni. Następnie możesz zacząć szukać sposobów dostrojenia bazy danych (nie zapytania). Popraw składnię tylko wtedy, gdy to się nie powiedzie.

Jak w przypadku każdego dostrajania wydajności, zawsze zbieraj znaczące statystyki. Nie używaj zegara ściennego, chyba że dostosowujesz ustawienia użytkownika. Zamiast tego spójrz na takie rzeczy, jak czas procesora, pobrane wiersze i bloki odczytane z dysku. Zbyt często ludzie optymalizują się pod kątem niewłaściwych rzeczy.

Jon Ericson
źródło
2

Pierwszy krok: spójrz na plan wykonania zapytań!
TableScan -> bad
NestedLoop -> meh warning
TableScan behind a NestedLoop -> DOOM!

USTAW STATYSTYKI IO WŁ.
USTAW CZAS STATYSTYK WŁĄCZONY

Amy B.
źródło
2

Uruchamianie zapytania za pomocą WITH (NoLock) jest w moim przypadku standardową operacją. Każdy przyłapany na wykonywaniu zapytań na dziesiątkach gigabajtów tabel bez tego jest usuwany i rozstrzeliwany.

Valerion
źródło
2
Powinno to być stosowane rozsądnie, a nie zwyczajowo. Blokowanie nie jest złe, po prostu źle zrozumiane.
2

Jeśli to możliwe, konwertuj zapytania NOT IN na LEFT OUTER JOINS. Na przykład, jeśli chcesz znaleźć wszystkie wiersze w tabeli Tabela1, które nie są używane przez klucz obcy w tabeli2, możesz to zrobić:

SELECT *
FROM Table1
WHERE Table1.ID NOT IN (
    SELECT Table1ID
    FROM Table2)

Ale dzięki temu uzyskasz znacznie lepszą wydajność:

SELECT Table1.*
FROM Table1
LEFT OUTER JOIN Table2 ON Table1.ID = Table2.Table1ID
WHERE Table2.ID is null
Martin Brown
źródło
1

@ DavidM

Zakładając tutaj MySQL, użyj EXPLAIN, aby dowiedzieć się, co się dzieje z zapytaniem, upewnij się, że indeksy są używane tak wydajnie, jak to możliwe ...

W SQL Server plan wykonania daje to samo - informuje o tym, jakie indeksy są atakowane itp.

Seibar
źródło
1

Zindeksuj tabelę (y) przez clm (y) według których filtrujesz

csmba
źródło
1

Niekoniecznie jest to sztuczka wydajnościowa SQL jako taka, ale zdecydowanie związana:

Dobrym pomysłem byłoby użycie memcached tam, gdzie to możliwe, ponieważ byłoby znacznie szybsze pobieranie wstępnie skompilowanych danych bezpośrednio z pamięci, niż pobieranie ich z bazy danych. Istnieje również odmiana MySQL z wbudowanym memcached (strona trzecia).

Andy
źródło
1

Upewnij się, że długość indeksu jest jak najmniejsza. Pozwala to DB odczytać więcej kluczy na raz z systemu plików, przyspieszając w ten sposób łączenie. Zakładam, że działa to ze wszystkimi bazami danych, ale wiem, że jest to konkretna rekomendacja dla MySQL.

Barrett Conrad
źródło
1

Zwracam uwagę na:

  • Rozwiń dowolne pętle CURSOR i przekonwertuj na zestaw instrukcji UPDATE / INSERT.
  • Zwróć uwagę na kod aplikacji, który:
    • Wzywa SP, który zwraca duży zestaw rekordów,
    • Następnie w aplikacji przechodzi przez każdy rekord i wywołuje SP z parametrami w celu aktualizacji rekordów.
    • Zamień to na SP, który wykonuje całą pracę w jednej transakcji.
  • Każdy SP, który wykonuje wiele operacji na strunach. Jest to dowód, że dane nie mają prawidłowej struktury / znormalizowania.
  • Każdy SP, który na nowo wymyśla koło.
  • Wszelkie SP, których nie mogę zrozumieć, co próbuje zrobić w ciągu minuty!
Chłopak
źródło
1
SET NOCOUNT ON

Zwykle jest to pierwsza linia w moich procedurach składowanych, chyba że faktycznie muszę użyć @@ROWCOUNT.

travis
źródło
2
@@ ROWCOUNT jest mimo to ustawione. NOCOUNT wyłącza instrukcje „dotkniętych wierszy xx”.
Sklivvz
Czy naprawdę kiedykolwiek robi to zauważalną różnicę w wydajności?
JohnFx
Tak, liczba nie jest obliczana automatycznie za każdym razem, gdy wykonywana jest instrukcja SQL. Łatwo jest porównać zapytanie zi bez, aby zobaczyć, że robi to różnicę.
travis
Licznik i tak jest śledzony w SQL Server. Każda różnica w wydajności, którą widzisz, jest spowodowana tym, że liczby muszą przechodzić przez sieć do interfejsu użytkownika. Jeśli wykonujesz pojedynczy SELECT, nie będzie to znaczącej różnicy. Jeśli masz pętlę z 100000 wstawkami, to w sieci jest dużo więcej.
Tom H
1

W SQL Server użyj dyrektywy nolock. Pozwala na wykonanie polecenia select bez czekania - zwykle na zakończenie innych transakcji.

SELECT * FROM Orders (nolock) where UserName = 'momma'
jinsungy
źródło
3
NOLOCK jest przeznaczony tylko do zapytań, dla których nie zależy Ci na poprawnych wynikach
Mark Sowul
1

Usuń kursory wszędzie tam, gdzie nie jest to konieczne.

Terrapin
źródło
Tak, kursory to przekleństwo! ;)
Sklivvz
8
Fuj. Nie wyrzucaj tego bez kwalifikacji. Kursory są jak pistolety. Same w sobie nie są złe, po prostu ludzie robią z nimi naprawdę złe rzeczy.
JohnFx
1

Usuń wywołania funkcji w Sprocs, gdzie wiele wierszy będzie wywoływać funkcję.

Mój kolega użył wywołań funkcji (na przykład pobierając lastlogindate z identyfikatora użytkownika), aby zwrócić bardzo szerokie zestawy rekordów.

Mając zadanie optymalizacji, zastąpiłem wywołania funkcji w sproc kodem funkcji: zmniejszyłem czas działania wielu sproców z> 20 sekund do <1.

callisto
źródło
0
  • Przedrostek wszystkich tabel z dbo. aby zapobiec ponownej kompilacji.
  • Przeglądaj plany zapytań i szukaj skanów tabel / indeksów.
  • W 2005 r. Przejrzyj widoki zarządzania pod kątem brakujących indeksów.
Stu
źródło
0

Nie poprzedzaj nazw procedur składowanych przedrostkiem „sp_”, ponieważ wszystkie procedury systemowe zaczynają się od „sp_”, a SQL Server będzie musiał dokładniej szukać procedury, gdy zostanie wywołana.

Terrapin
źródło
1
Czy faktycznie porównałeś ten? Jeśli SQL Server robi to, co jest rozsądne (używając algorytmu skrótu do zlokalizowania procesu składowanego), nie miałoby to znaczenia. W rzeczywistości, gdyby SQL Server tego nie robił, wydaje się, że wydajność systemu byłaby śmierdząca (ponieważ prawdopodobnie wywołuje własne procesy).
John Stauffer
1
Myślę, że to wpisuje się w zakres przedwczesnej optymalizacji. Prawdopodobnie dobrą praktyką jest unikanie zamieszania wśród ludzi, ale jako wskazówka optymalizacyjna ... D-
JohnFx
0

Brudne czyta -

set transaction isolation level read uncommitted

Zapobiega martwym blokadom, w których integralność transakcyjna nie jest absolutnie konieczna (co zwykle jest prawdą)

Terrapin
źródło
1
Tak, ale może to prowadzić do dziwnych błędów, które BARDZO trudno jest znaleźć.
Grant Johnson
0

Zawsze najpierw przechodzę do programu SQL Profiler (jeśli jest to procedura składowana z wieloma poziomami zagnieżdżania) lub planera wykonywania zapytań (jeśli jest to kilka instrukcji SQL bez zagnieżdżenia). W 90% przypadków można natychmiast znaleźć problem za pomocą jednego z tych dwóch narzędzi.

mwigdahl
źródło