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

Uważaj na wprowadzające w błąd dane z SET STATISTICS IO

Mój współpracownik Steve Wright (blog | @SQL_Steve) zadał mi ostatnio pytanie dotyczące dziwnego wyniku, który widział. Aby przetestować niektóre funkcje naszego najnowszego narzędzia, SQL Sentry Plan Explorer PRO, stworzył szeroką i dużą tabelę i uruchamiał wobec niej różne zapytania. W jednym przypadku zwracał dużo danych, ale STATISTICS IO pokazywał, że odbyło się bardzo mało odczytów. Zadzwoniłem do kilku osób na #sqlhelp i ponieważ wydawało mi się, że nikt nie widział tego problemu, pomyślałem, że napiszę o tym na blogu.

TL;DR Wersja

Krótko mówiąc, miej świadomość, że istnieją pewne scenariusze, w których nie możesz polegać na STATISTICS IO powiedzieć ci prawdę. W niektórych przypadkach (ten z udziałem TOP i równoległości), znacznie zaniży liczbę odczytów logicznych. Może to prowadzić do przekonania, że ​​masz bardzo przyjazne dla I/O zapytanie, gdy tego nie zrobisz. Istnieją inne, bardziej oczywiste przypadki — na przykład, gdy masz ukrytą masę operacji we/wy za pomocą skalarnych funkcji zdefiniowanych przez użytkownika. Uważamy, że Eksplorator Planu czyni te przypadki bardziej oczywistymi; ten jest jednak nieco trudniejszy.

Zapytanie problemowe

Tabela ma 37 milionów wierszy, do 250 bajtów na wiersz, około 1 miliona stron i bardzo niską fragmentację (0,42% na poziomie 0, 15% na poziomie 1 i 0 poza tym). Nie ma kolumn obliczanych, żadnych funkcji UDF ani indeksów z wyjątkiem klastrowanego klucza podstawowego na wiodącym INT kolumna. Proste zapytanie zwracające 500 000 wierszy, wszystkie kolumny, przy użyciu TOP i SELECT * :

SET STATISTICS IO ON;
 
SELECT TOP 500000 * FROM dbo.OrderHistory 
WHERE OrderDate < (SELECT '19961029');

(I tak, zdaję sobie sprawę, że łamię własne zasady i używam SELECT * i TOP bez ORDER BY , ale dla uproszczenia staram się jak najlepiej zminimalizować mój wpływ na optymalizator.)

Wyniki:

(500000 wierszy, których dotyczy problem)
Tabela „Historia zamówień”. Liczba skanów 1, odczyty logiczne 23, odczyty fizyczne 0, odczyty z wyprzedzeniem 0, odczyty logiczne lobu 0, odczyty fizyczne lobu 0, odczyty loba z wyprzedzeniem 0.

Zwracamy 500 000 wierszy i zajmuje to około 10 sekund. Od razu wiem, że coś jest nie tak z liczbą odczytów logicznych. Nawet jeśli nie wiedziałem jeszcze o danych źródłowych, mogę stwierdzić z wyników siatki w Management Studio, że pobiera to ponad 23 strony danych, niezależnie od tego, czy pochodzą one z pamięci, czy z pamięci podręcznej, i powinno to znaleźć odzwierciedlenie gdzieś w STATISTICS IO . Patrząc na plan…

…widzimy, że jest tam równoległość i że przeskanowaliśmy cały stół. Więc jak to możliwe, że są tylko 23 logiczne odczyty?

Inne „identyczne” zapytanie

Jedno z moich pierwszych pytań do Steve'a brzmiało:„Co się stanie, jeśli wyeliminujesz równoległość?” Więc spróbowałem. Wziąłem oryginalną wersję podzapytania i dodałem MAXDOP 1 :

SET STATISTICS IO ON;
 
SELECT TOP 500000 * FROM dbo.OrderHistory 
WHERE OrderDate < (SELECT '19961029') OPTION (MAXDOP 1);

Wyniki i plan:

(500000 wierszy, których dotyczy problem)
Tabela „Historia zamówień”. Liczba skanów 1, odczyty logiczne 149589, odczyty fizyczne 0, odczyty z wyprzedzeniem 0, odczyty logiczne modułu 0, odczyty fizyczne modułu 0, odczyty modułu z wyprzedzeniem 0.

Mamy nieco mniej skomplikowany plan i bez paralelizmu (z oczywistych względów), STATISTICS IO pokazuje nam znacznie bardziej wiarygodne liczby dla logicznej liczby odczytów.

Jaka jest prawda?

Nietrudno zauważyć, że jedno z tych pytań nie mówi całej prawdy. Podczas gdy STATISTICS IO może nie opowiedzieć nam całej historii, może ślad będzie. Jeśli pobierzemy metryki środowiska wykonawczego, generując rzeczywisty plan wykonania w Eksploratorze planów, zobaczymy, że magiczne zapytanie o niskim poziomie odczytu w rzeczywistości pobiera dane z pamięci lub dysku, a nie z chmury magicznego pyłu. W rzeczywistości ma *więcej* odczytów niż inna wersja:

Jasne jest więc, że odczyty mają miejsce, po prostu nie wyświetlają się poprawnie w STATISTICS IO wyjście.

W czym problem?

Cóż, będę całkiem szczery:nie wiem, poza faktem, że równoległość zdecydowanie odgrywa rolę i wydaje się, że jest to jakiś rodzaj rasy. STATISTICS IO (a ponieważ stąd otrzymujemy dane, nasza zakładka Table I/O) pokazuje bardzo mylącą liczbę odczytów. Jasne jest, że zapytanie zwraca wszystkie dane, których szukamy, i z wyników śledzenia jasno wynika, że ​​używa do tego odczytów, a nie osmozy. Zapytałem o to Paula White'a (blog | @SQL_Kiwi) i zasugerował, że tylko niektóre liczniki we/wy przed wątkiem są uwzględniane w sumie (i zgadza się, że jest to błąd).

Jeśli chcesz wypróbować to w domu, potrzebujesz tylko AdventureWorks (powinno to powtórzyć wersje 2008, 2008 R2 i 2012) i następującego zapytania:

SET STATISTICS IO ON;
DBCC SETCPUWEIGHT(1000) WITH NO_INFOMSGS;
GO
 
SELECT TOP (15000) * 
FROM Sales.SalesOrderHeader 
WHERE OrderDate < (SELECT '20080101');
 
SELECT TOP (15000) * 
FROM Sales.SalesOrderHeader 
WHERE OrderDate < (SELECT '20080101') 
OPTION (MAXDOP 1);
 
DBCC SETCPUWEIGHT(1) WITH NO_INFOMSGS;

(Zauważ, że SETCPUWEIGHT służy tylko do nakłaniania równoległości. Aby uzyskać więcej informacji, zobacz post na blogu Paula White'a na temat kalkulacji kosztów planu).

Wyniki:

Tabela „Nagłówek zamówienia sprzedaży”. Liczba skanów 1, odczyty logiczne 4, odczyty fizyczne 0, odczyty z wyprzedzeniem 0, odczyty logiczne lobu 0, odczyty fizyczne lobu 0, odczyty z wyprzedzeniem lobu 0.
Tabela „SalesOrderHeader”. Liczba skanów 1, odczyty logiczne 333, odczyty fizyczne 0, odczyty z wyprzedzeniem 0, odczyty logiczne LOB 0, odczyty fizyczne LOB 0, odczyty LOB z wyprzedzeniem 0.

Paul wskazał na jeszcze prostsze powtórzenie:

SET STATISTICS IO ON;
GO
 
SELECT TOP (15000) *
FROM Production.TransactionHistory
WHERE TransactionDate < (SELECT '20080101')
OPTION (QUERYTRACEON 8649, MAXDOP 4);
 
SELECT TOP (15000) *
FROM Production.TransactionHistory AS th
WHERE TransactionDate < (SELECT '20080101');

Wyniki:

Tabela „Historia transakcji”. Liczba skanów 1, odczyty logiczne 5, odczyty fizyczne 0, odczyty z wyprzedzeniem 0, odczyty logiczne lobu 0, odczyty fizyczne lobu 0, odczyty z wyprzedzeniem lobu 0.
Tabela „Historia transakcji”. Liczba skanów 1, odczyty logiczne 110, odczyty fizyczne 0, odczyty z wyprzedzeniem 0, odczyty logiczne lobu 0, odczyty fizyczne lobu 0, odczyty loba z wyprzedzeniem 0.

Wygląda więc na to, że możemy łatwo odtworzyć to do woli za pomocą TOP operatora i wystarczająco niski DOP. Zgłosiłem błąd:

  • STATYSTYKI IO zgłasza niedoszacowanie odczytów logicznych dla planów równoległych

A Paul zgłosił dwa inne, nieco powiązane błędy dotyczące paralelizmu, pierwszy w wyniku naszej rozmowy:

  • Błąd oszacowania kardynalności z predykatem wypchniętym w wyszukiwaniu [powiązany wpis na blogu]
  • Słaba wydajność z równoległością i Top [powiązany wpis na blogu]

(Dla nostalgii, oto sześć innych błędów paralelizmu, które wskazałem kilka lat temu.)

Jaka jest lekcja?

Uważaj, aby ufać jednemu źródłu. Jeśli spojrzysz wyłącznie na STATISTICS IO po zmianie zapytania w ten sposób możesz pokusić się o skupienie się na cudownym spadku liczby odczytów zamiast na zwiększeniu czasu trwania. W tym momencie możesz poklepać się po plecach, wyjść wcześniej z pracy i cieszyć się weekendem, myśląc, że właśnie wywarłeś ogromny wpływ na wydajność zapytania. Oczywiście nic nie może być dalsze od prawdy.


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Model danych warsztatu samochodowego

  2. SQL Union — kompleksowy przewodnik po operatorze UNION

  3. Wskazówki dotyczące lepszego projektowania baz danych

  4. SQL DELETE dla początkujących

  5. Więcej ulepszeń showplanu? Tak proszę!