Database
 sql >> Baza danych >  >> RDS >> Database

3 paskudne statystyki we/wy, które opóźniają wydajność zapytań SQL

„Ale na naszym serwerze programistycznym działało dobrze!”

Ile razy słyszałem, gdy tu i tam wystąpiły problemy z wydajnością zapytań SQL? Powiedziałem to sam kiedyś. Założyłem, że zapytanie uruchomione w mniej niż sekundę będzie działać dobrze na serwerach produkcyjnych. Ale myliłem się.

Czy możesz odnieść się do tego doświadczenia? Jeśli z jakiegoś powodu nadal jesteś na tej łodzi, ten post jest dla Ciebie. Zapewni to lepszą metrykę dostrajania wydajności zapytań SQL. Porozmawiamy o trzech najbardziej krytycznych danych w STATISTICS IO.

Jako przykład użyjemy przykładowej bazy danych AdventureWorks.

Zanim zaczniesz uruchamiać poniższe zapytania, włącz STATISTICS IO. Oto jak to zrobić w oknie zapytania:

USE AdventureWorks
GO

SET STATISTICS IO ON

Po uruchomieniu zapytania z włączonym STATISTICS IO ON, pojawią się różne komunikaty. Możesz je zobaczyć w zakładce Wiadomości w oknie zapytania w SQL Server Management Studio (patrz Rysunek 1):

Teraz, gdy skończyliśmy z krótkim wprowadzeniem, zajmijmy się głębiej.

1. Wysokie odczyty logiczne

Pierwszy punkt na naszej liście to najczęstszy winowajca – wysokie odczyty logiczne.

Odczyty logiczne to liczba stron odczytanych z pamięci podręcznej danych. Strona ma rozmiar 8 KB. Z drugiej strony pamięć podręczna danych odnosi się do pamięci RAM używanej przez SQL Server.

Odczyty logiczne mają kluczowe znaczenie dla dostrajania wydajności. Ten czynnik określa, ile SQL Server potrzebuje do wygenerowania wymaganego zestawu wyników. Dlatego jedyną rzeczą do zapamiętania jest to, że im wyższe są odczyty logiczne, tym dłużej SQL Server musi działać. Oznacza to, że Twoje zapytanie będzie wolniejsze. Zmniejsz liczbę odczytów logicznych, a zwiększysz wydajność zapytań.

Ale po co używać odczytów logicznych zamiast czasu, który upłynął?

  • Czas, który upłynął, zależy od innych czynności wykonywanych przez serwer, nie tylko od samego zapytania.
  • Upływający czas może zmienić się z serwera deweloperskiego na serwer produkcyjny. Dzieje się tak, gdy oba serwery mają różne pojemności oraz konfiguracje sprzętu i oprogramowania.

Poleganie na upływającym czasie spowoduje, że powiesz:„Ale na naszym serwerze programistycznym działało dobrze!” prędzej czy później.

Po co używać odczytów logicznych zamiast odczytów fizycznych?

  • Odczyty fizyczne to liczba stron odczytanych z dysków do pamięci podręcznej danych (w pamięci). Gdy strony potrzebne w zapytaniu znajdą się w pamięci podręcznej danych, nie ma potrzeby ponownego odczytywania ich z dysków.
  • Gdy to samo zapytanie zostanie ponownie uruchomione, fizyczne odczyty wyniosą zero.

Odczyty logiczne są logicznym wyborem do dostrajania wydajności zapytań SQL.

Aby zobaczyć to w akcji, przejdźmy do przykładu.

Przykład odczytów logicznych

Załóżmy, że musisz uzyskać listę klientów z zamówieniami wysłanymi 11 lipca 2011 r. Poniżej przedstawiasz dość proste zapytanie:

SELECT
 d.FirstName
,d.MiddleName
,d.LastName
,d.Suffix
,a.OrderDate
,a.ShipDate
,a.Status
,b.ProductID
,b.OrderQty
,b.UnitPrice
FROM Sales.SalesOrderHeader a
INNER JOIN Sales.SalesOrderDetail b ON a.SalesOrderID = b.SalesOrderID
INNER JOIN Sales.Customer c ON a.CustomerID = c.CustomerID
INNER JOIN Person.Person d ON c.PersonID = D.BusinessEntityID
WHERE a.ShipDate = '07/11/2011'

To proste. To zapytanie będzie miało następujący wynik:

Następnie sprawdzasz wynik STATISTICS IO tego zapytania:

Dane wyjściowe przedstawiają odczyty logiczne każdej z czterech tabel użytych w zapytaniu. W sumie suma odczytów logicznych wynosi 729. Możesz również zobaczyć odczyty fizyczne z łączną sumą 21. Spróbuj jednak ponownie uruchomić zapytanie, a wyniesie zero.

Przyjrzyj się logicznym odczytom SalesOrderHeader . Zastanawiasz się, dlaczego ma 689 logicznych odczytów? Być może myślałeś o sprawdzeniu poniższego planu wykonania zapytania:

Po pierwsze, istnieje skanowanie indeksu, które miało miejsce w SalesOrderHeader z 93% kosztem. Co się może dziać? Załóżmy, że sprawdziłeś jego właściwości:

Łał! 31 465 wierszy przeczytanych, a zwróconych tylko 5 wierszy? To absurd!

Zmniejszanie liczby odczytów logicznych

Nie jest tak trudno zmniejszyć te 31 465 przeczytanych wierszy. SQL Server już dał nam wskazówkę. Przejdź do następujących czynności:

KROK 1:Postępuj zgodnie z zaleceniami SQL Server i dodaj brakujący indeks

Czy zauważyłeś brakującą rekomendację indeksu w planie realizacji (Rysunek 4)? Czy to rozwiąże problem?

Jest jeden sposób, aby się tego dowiedzieć:

CREATE NONCLUSTERED INDEX [IX_SalesOrderHeader_ShipDate]
ON [Sales].[SalesOrderHeader] ([ShipDate])

Uruchom ponownie zapytanie i zobacz zmiany w odczytach logicznych STATISTICS IO.

Jak widać w STATISTICS IO (Rysunek 6), nastąpił ogromny spadek odczytów logicznych z 689 do 17. Nowe ogólne odczyty logiczne to 57, co stanowi znaczną poprawę w porównaniu z 729 odczytami logicznymi. Ale dla pewności ponownie sprawdźmy plan wykonania.

Wygląda na to, że plan uległ ulepszeniu, powodując zmniejszenie odczytów logicznych. Skanowanie indeksu jest teraz wyszukiwaniem indeksu. SQL Server nie będzie już musiał sprawdzać wiersz po wierszu, aby uzyskać rekordy z Shipdate=’07/11/2011′ . Ale coś wciąż czai się w tym planie i nie jest w porządku.

Potrzebujesz kroku 2.

KROK 2:Zmień indeks i dodaj do uwzględnionych kolumn:OrderDate, Status i CustomerID

Czy widzisz ten operator Key Lookup w planie wykonania (Rysunek 7)? Oznacza to, że utworzony indeks nieklastrowy nie wystarczy – procesor zapytań musi ponownie użyć indeksu klastrowego.

Sprawdźmy jego właściwości.

Zwróć uwagę na dołączone pole pod Listą wyników . Zdarza się, że potrzebujemy DataZamówienia , Stan i Identyfikator klienta w zestawie wyników. Aby uzyskać te wartości, procesor zapytań użył indeksu klastrowego (patrz Predykaty wyszukiwania ), aby dostać się do stołu.

Musimy usunąć to Key Lookup. Rozwiązaniem jest uwzględnienie DataZamówienia , Stan i Identyfikator klienta kolumny do utworzonego wcześniej indeksu.

  • Kliknij prawym przyciskiem myszy IX_SalesOrderHeader_ShipDate w SSMS.
  • Wybierz Właściwości .
  • Kliknij Kolumny uwzględnione zakładka.
  • Dodaj datę zamówienia , Stan i Identyfikator klienta .
  • Kliknij OK .

Po ponownym utworzeniu indeksu uruchom ponownie zapytanie. Czy spowoduje to usunięcie Key Lookup? i zmniejszyć logiczne odczyty?

Zadziałało! Od 17 logicznych odczytów do 2 (Rysunek 9).

Oraz Wyszukiwanie klucza ?

Odeszło! Przeszukiwanie indeksu klastrowego zastąpił Key Lookup.

Na wynos

Czego więc się nauczyliśmy?

Jednym z podstawowych sposobów ograniczenia odczytów logicznych i poprawy wydajności zapytań SQL jest utworzenie odpowiedniego indeksu. Ale jest pewien haczyk. W naszym przykładzie zmniejszyło to odczyty logiczne. Czasami będzie odwrotnie. Może to również wpłynąć na wydajność innych powiązanych zapytań.

Dlatego po utworzeniu indeksu zawsze sprawdzaj STATISTICS IO i plan wykonania.

2. Logiczne odczyty wysokiego lobu

Jest bardzo podobny do punktu 1, ale dotyczy typów danych tekst , ntekst , obraz , warcha (maks. ), nvarchar (maks. ), zmienna (maks. ) lub sklep z kolumnami strony indeksu.

Odwołajmy się do przykładu:generowanie logicznych odczytów lob.

Przykład logicznych odczytów Lob

Załóżmy, że chcesz wyświetlić produkt z jego ceną, kolorem, miniaturą i większym obrazem na stronie internetowej. W ten sposób pojawia się wstępne zapytanie, jak pokazano poniżej:

SELECT
 a.ProductID
,a.Name AS ProductName
,a.ListPrice
,a.Color
,b.Name AS ProductSubcategory
,d.ThumbNailPhoto
,d.LargePhoto
FROM Production.Product a
INNER JOIN Production.ProductSubcategory b ON a.ProductSubcategoryID = b.ProductSubcategoryID
INNER JOIN Production.ProductProductPhoto c ON a.ProductID = c.ProductID
INNER JOIN Production.ProductPhoto d ON c.ProductPhotoID = d.ProductPhotoID
WHERE b.ProductCategoryID = 1 -- Bikes
ORDER BY ProductSubcategory, ProductName, a.Color

Następnie uruchamiasz go i widzisz wynik taki jak ten poniżej:

Jako, że jesteś takim facetem o wysokich osiągach (lub gal), natychmiast sprawdzasz STATISTICS IO. Oto on:

Czujesz jak brud w twoich oczach. 665 logicznych odczytów lobów? Nie możesz tego zaakceptować. Nie wspominając o 194 logicznych odczytach, każdy z ProductPhoto i ProductPhoto tabele. Naprawdę uważasz, że to zapytanie wymaga pewnych zmian.

Zmniejszanie logicznych odczytów lobby

Poprzednie zapytanie zwróciło 97 wierszy. Wszystkie 97 rowerów. Czy uważasz, że dobrze jest wyświetlać to na stronie internetowej?

Indeks może pomóc, ale dlaczego najpierw nie uprościć zapytania? W ten sposób możesz decydować o tym, co zwróci SQL Server. Możesz zmniejszyć logiczne odczyty lob.

  • Dodaj filtr dla podkategorii produktu i pozwól klientowi dokonać wyboru. Następnie uwzględnij to w klauzuli WHERE.
  • Usuń podkategorię produktu kolumnę, ponieważ dodasz filtr dla podkategorii produktu.
  • Usuń Duże zdjęcie kolumna. Zapytaj o to, gdy użytkownik wybierze określony produkt.
  • Użyj stronicowania. Klient nie będzie mógł wyświetlić wszystkich 97 rowerów jednocześnie.

Na podstawie tych operacji opisanych powyżej zmieniamy zapytanie w następujący sposób:

  • Usuń podkategorię produktu i Duże zdjęcie kolumny ze zbioru wyników.
  • Użyj opcji OFFSET i FETCH, aby uwzględnić stronicowanie w zapytaniu. Zapytaj tylko 10 produktów na raz.
  • Dodaj ID podkategorii produktu w klauzuli WHERE na podstawie wyboru klienta.
  • Usuń podkategorię produktu kolumna w klauzuli ORDER BY.

Zapytanie będzie teraz podobne do tego:

DECLARE @pageNumber TINYINT
DECLARE @noOfRows TINYINT =  10 -- each page will display 10 products at a time

SELECT
 a.ProductID
,a.Name AS ProductName
,a.ListPrice
,a.Color
,d.ThumbNailPhoto
FROM Production.Product a
INNER JOIN Production.ProductSubcategory b ON a.ProductSubcategoryID = b.ProductSubcategoryID
INNER JOIN Production.ProductProductPhoto c ON a.ProductID = c.ProductID
INNER JOIN Production.ProductPhoto d ON c.ProductPhotoID = d.ProductPhotoID
WHERE b.ProductCategoryID = 1 -- Bikes
AND a.ProductSubcategoryID = 2 -- Road Bikes
ORDER BY ProductName, a.Color
OFFSET (@pageNumber-1)*@noOfRows ROWS FETCH NEXT @noOfRows ROWS ONLY     
-- change the OFFSET and FETCH values based on what page the user is.

Czy po wprowadzeniu zmian odczyty logiczne lobów ulegną poprawie? STATISTICS IO teraz zgłasza:

Zdjęcie produktu tabela ma teraz 0 logicznych odczytów loba — od 665 logicznych odczytów loba do żadnego. To jest pewna poprawa.

Na wynos

Jednym ze sposobów zmniejszenia logicznych odczytów lob jest przepisanie zapytania w celu jego uproszczenia.

Usuń niepotrzebne kolumny i zmniejsz zwracane wiersze do najmniej wymaganej. W razie potrzeby użyj funkcji OFFSET i FETCH do stronicowania.

Aby upewnić się, że zmiany w zapytaniu poprawiły odczyty logiczne lob i wydajność zapytań SQL, zawsze sprawdzaj STATISTICS IO.

3. Wysokie odczyty logiczne tabeli roboczej/pliku roboczego

Wreszcie, to logiczne odczyty Worktable i Plik roboczy . Ale czym są te stoły? Dlaczego pojawiają się, gdy nie używasz ich w zapytaniu?

Posiadanie Stół roboczy i Plik roboczy pojawiające się w STATISTICS IO oznacza, że ​​SQL Server potrzebuje dużo więcej pracy, aby uzyskać pożądane rezultaty. Wykorzystuje tabele tymczasowe w tempdb , czyli Stoły robocze i Pliki robocze . Niekoniecznie szkodliwe jest posiadanie ich na wyjściu STATISTICS IO, o ile odczyty logiczne wynoszą zero i nie powoduje to problemów dla serwera.

Te tabele mogą pojawiać się między innymi, gdy występuje ORDER BY, GROUP BY, CROSS JOIN lub DISTINCT.

Przykład odczytów logicznych z tabeli roboczej/pliku roboczego

Załóżmy, że musisz wysłać zapytanie do wszystkich sklepów bez sprzedaży niektórych produktów.

Początkowo wymyślasz następujące rzeczy:

SELECT DISTINCT
 a.SalesPersonID
,b.ProductID
,ISNULL(c.OrderTotal,0) AS OrderTotal
FROM Sales.Store a
CROSS JOIN Production.Product b
LEFT JOIN (SELECT
            b.SalesPersonID
           ,a.ProductID
           ,SUM(a.LineTotal) AS OrderTotal
           FROM Sales.SalesOrderDetail a
           INNER JOIN Sales.SalesOrderHeader b ON a.SalesOrderID = b.SalesOrderID
           WHERE b.SalesPersonID IS NOT NULL
           GROUP BY b.SalesPersonID, a.ProductID, b.OrderDate) c ON a.SalesPersonID  
                                                                  = c.SalesPersonID
                                                  AND b.ProductID = c.ProductID
WHERE c.OrderTotal IS NULL
ORDER BY a.SalesPersonID, b.ProductID

To zapytanie zwróciło 3649 wierszy:

Sprawdźmy, co mówi IO STATYSTYKI:

Warto zauważyć, że Stół roboczy odczyty logiczne to 7128. Ogólny odczyt logiczny to 8853. Jeśli sprawdzisz plan wykonania, zobaczysz wiele paralelizmów, dopasowań haszujących, buforowania i skanowania indeksów.

Zmniejszanie logicznych odczytów z tabeli roboczej/pliku roboczego

Nie mogłem skonstruować pojedynczej instrukcji SELECT z satysfakcjonującym wynikiem. Zatem jedynym wyjściem jest rozbicie instrukcji SELECT na wiele zapytań. Zobacz poniżej:

SELECT DISTINCT
 a.SalesPersonID
,b.ProductID
INTO #tmpStoreProducts
FROM Sales.Store a
CROSS JOIN Production.Product b

SELECT
 b.SalesPersonID
,a.ProductID
,SUM(a.LineTotal) AS OrderTotal
INTO #tmpProductOrdersPerSalesPerson
FROM Sales.SalesOrderDetail a
INNER JOIN Sales.SalesOrderHeader b ON a.SalesOrderID = b.SalesOrderID
WHERE b.SalesPersonID IS NOT NULL
GROUP BY b.SalesPersonID, a.ProductID

SELECT
 a.SalesPersonID
,a.ProductID
FROM #tmpStoreProducts a
LEFT JOIN #tmpProductOrdersPerSalesPerson b ON a.SalesPersonID = b.SalesPersonID AND 
                                               a.ProductID = b.ProductID
WHERE b.OrderTotal IS NULL
ORDER BY a.SalesPersonID, a.ProductID

DROP TABLE #tmpProductOrdersPerSalesPerson
DROP TABLE #tmpStoreProducts

Jest o kilka wierszy dłuższy i używa tabel tymczasowych. Zobaczmy teraz, co ujawnia IO STATISTICS:

Staraj się nie skupiać na długości tego statystycznego raportu – to tylko frustrujące. Zamiast tego dodaj logiczne odczyty z każdej tabeli.

Łącznie 1279 oznacza znaczny spadek, ponieważ było to 8853 logicznych odczytów z pojedynczej instrukcji SELECT.

Nie dodaliśmy żadnego indeksu do tabel tymczasowych. Możesz go potrzebować, jeśli do SalesOrderHeader zostanie dodanych dużo więcej rekordów i Szczegóły zamówienia sprzedaży . Ale rozumiesz.

Na wynos

Czasami wyrażenie 1 SELECT wydaje się dobre. Jednak za kulisami jest odwrotnie. Stoły robocze i Pliki robocze z wysokimi odczytami logicznymi opóźniają wydajność zapytań SQL.

Jeśli nie możesz wymyślić innego sposobu na zrekonstruowanie zapytania, a indeksy są bezużyteczne, wypróbuj podejście „dziel i rządź”. Stoły robocze i Pliki robocze może nadal pojawiać się na karcie Wiadomość programu SSMS, ale odczyty logiczne będą wynosić zero. Dlatego ogólny wynik będzie mniej logiczny.

Konkluzja wydajności zapytań SQL i IO STATISTICS

O co chodzi z tymi 3 paskudnymi statystykami I/O?

Różnica w wydajności zapytań SQL będzie jak w dzień iw nocy, jeśli zwrócisz uwagę na te liczby i je zmniejszysz. Przedstawiliśmy tylko kilka sposobów na ograniczenie odczytów logicznych, takich jak:

  • tworzenie odpowiednich indeksów;
  • uproszczenie zapytań – usunięcie zbędnych kolumn i zminimalizowanie zestawu wyników;
  • podział zapytania na wiele zapytań.

Jest to bardziej jak aktualizowanie statystyk, defragmentowanie indeksów i ustawianie właściwego FILLFACTORa. Czy możesz dodać więcej do tego w sekcji komentarzy?

Jeśli podoba Ci się ten post, udostępnij go w swoich ulubionych mediach społecznościowych.


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Dlaczego optymalizator nie wykorzystuje wiedzy o puli buforów

  2. Łączenie SQuirreL SQL z Microsoft Excel

  3. SQL DROP Index, DROP Table i instrukcje bazy danych DROP wyjaśnione na przykładach

  4. Więcej o wprowadzeniu stref czasowych w długowiecznym projekcie

  5. Połączone serwery Salesforce.com i sp_columns_ex