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

Jak automatyczne aktualizacje statystyk mogą wpływać na wydajność zapytań

W moim poprzednim poście badałem różne metody śledzenia automatycznych aktualizacji statystyk w celu ustalenia, czy wpływają one na wydajność zapytań. W drugiej połowie postu zamieściłem opcje, z których jedną było włączenie ustawienia bazy danych Auto Update Statistics Asynchronously. W tym poście chcę przyjrzeć się, jak zmienia się wydajność zapytań, gdy automatyczna aktualizacja nastąpi przed wykonaniem zapytania i co się dzieje z wydajnością, jeśli aktualizacja jest asynchroniczna.

Konfiguracja

Zacząłem od kopii bazy danych AdventureWorks2012, a następnie za pomocą tego skryptu utworzyłem kopię tabeli SalesOrderHeader z ponad 200 milionami wierszy. Tabela ma indeks klastrowy na SalesOrderID i indeks nieklastrowany na CustomerID, OrderDate, SubTotal. [Uwaga:jeśli zamierzasz wykonywać powtarzane testy, wykonaj kopię zapasową tej bazy danych w tym momencie, aby zaoszczędzić trochę czasu]. Po załadowaniu danych i utworzeniu indeksu nieklastrowego zweryfikowałem liczbę wierszy i obliczyłem, ile wierszy (w przybliżeniu) wymagałoby modyfikacji, aby wywołać automatyczną aktualizację.

SELECTOBJECT_NAME([p].[id_obiektu]) [TableName],[si].[name] [IndexName],[au].[type_desc] [Type],[p].[rows] [RrowCount], ([p].[wiersze]*.20) + 500 [Próg aktualizacji],[au].total_pages [PageCount],(([au].[total_pages]*8)/1024)/1024 [Total GB]FROM [sys ].[partycje] [p]JOIN [sys].[allocation_units] [au] ON [p].[partition_id] =[au].[container_id]JOIN [sys].[indeksy] [si] na [p] .[object_id] =[si].object_id i [p].[index_id] =[si].[index_id]GDZIE [p].[object_id] =OBJECT_ID(N'Sales.Big_SalesOrderHeader');


Informacje o CIX i NCI Big_SalesOrderHeader

Zweryfikowałem również aktualny nagłówek statystyk indeksu:

DBCC SHOW_STATISTICS ('Sales.Big_SalesOrderHeader',[IX_Big_SalesOrderHeader_CustomerID_OrderDate_SubTotal]);


Statystyki NCI:na początku

Następnie stworzyłem procedurę składowaną, której użyję do testowania. Jest to prosta procedura, która wysyła zapytania do Sales.Big_SalesOrderHeader i agreguje dane sprzedaży według CustomerID i OrderDate do analizy:

UTWÓRZ PROCEDURĘ Sales.usp_GetCustomerStats@CustomerID INT,@StartDate DATETIME,@EndDate DATETIMEASBEGIN SET NOCOUNT ON; SELECT CustomerID, DATEPART(YEAR, OrderDate), DATEPART(Miesiąc, OrderDate), COUNT([SalesOrderID]) jako obliczone FROM [Sales].[Big_SalesOrderHeader] WHERE CustomerID =@CustomerID AND OrderDate BETWEEN @StartDate i @EndDate GROUP BY CustomerID, DATEPART(ROK, DataZamówienia), DATEPART(MIESIĄC, DataZamówienia) ORDER BY DATEPART(ROK, DataZamówienia), DATEPART(MIESIĄC, DataZamówienia);END

Wreszcie, przed wykonaniem procedury składowanej, utworzyłem sesję Extended Events, aby móc śledzić czas trwania zapytania za pomocą sp_statement_starting i sp_statement_completed. Dodałem również zdarzenie auto_stats, ponieważ chociaż nie spodziewałem się, że nastąpi aktualizacja, chciałem później użyć tej samej definicji sesji.

UTWÓRZ SESJĘ ZDARZENIA [StatsUpdate_QueryPerf] NA SERWERA DODANE ZDARZENIE sqlserver.auto_stats,DODAJ ZDARZENIE sqlserver.sp_statement_completed(SET collect_statement=(1)),DODAJ ZDARZENIE sqlserver.sp_statement_starting ADD TARGET package0.event\N'(SET nazwa_pliku StatsUpdate_QueryPerf.xel')WITH (MAX_MEMORY=4096 KB,EVENT_RETENTION_MODE=ALLOW_SINGLE_EVENT_LOSS,MAX_DISPATCH_LATENCY=30 SECONDS,MAX_EVENT_SIZE=0 KB,MEMORY_PARTITION_MODE=NONE,TRACK_CAUPALITY=NONE,TRACK_CAUSALITY=)Test 

Rozpocząłem sesję zdarzeń rozszerzonych, a następnie wielokrotnie wykonałem procedurę składowaną, używając różnych identyfikatorów CustomerID:

 ALTER EVENT SESSION [StatsUpdate_QueryPerf]ON SERVERSTATE =START;GO EXEC Sales.usp_GetCustomerStats 11331, '2012-08-01 00:00:00.000', '2012-08-31 23:59:59.997'GOEXEC Sales.usp_GetCustomerStats 11330, '2013-01-01 00:00:00.000', '2013-01-31 23:59:59,997'GOEXEC Sales.usp_GetCustomerStats 11506, '2012-11-01 00:00:00.000', '2012-11 -30 23:59:59.997'GOEXEC Sales.usp_GetCustomerStats 17061, '2013-01-01 00:00:00.000', '2013-01-31 23:59:59.997'GOEXEC Sales.usp_GetCustomerStats 11711, '2013-03- 01 00:00:00.000', '2013-03-31 23:59:59.997'GOEXEC Sales.usp_GetCustomerStats 15131, '2013-02-01 00:00:00.000', '2013-02-28 23:59:59,997 'Sprzedaż GOEXEC.usp_GetCustomerStats 29837, '2012-10-01 00:00:00.000', '2012-10-31 23:59:59,997'GOEXEC Sales.usp_GetCustomerStats 15750, '2013-03-01 00:00:00.000' , '2013-03-31 23:59:59,997'GO

Zweryfikowałem liczbę wykonań i plan, odpytując pamięć podręczną procedur:

SELECTOBJECT_NAME([st].[id obiektu]),[st].[tekst],[qs].[liczba_wykonań],[qs].[czas_utworzenia],[qs].[czas_ostatniego_wykonania],[qs". [min_czas_pracownika],[qs].[max_czas_pracownika],[qs].[min_odczyty_logiczne],[qs].[max_odczyty_logiczne],[qs].[min_upływający_czas],[qs].[max_czas_upływający],[qp].[zapytanie_plan ]Z [sys].[dm_exec_query_stats] [qs]CROSS APPLY [sys].[dm_exec_sql_text]([qs].plan_handle) [st]CROSS APPLY [sys].[dm_exec_query_plan]([qs].plan_handle) [qp] WHERE [st].[text] LIKE '%usp_GetCustomerStats%'AND OBJECT_NAME([st].[objectid]) NIE JEST NULL;


Pamięć podręczna planu:na początku


Plan zapytań dla procedury składowanej przy użyciu Eksploratora planów SQL Sentry

Widziałem, że plan powstał 08.04.2014 18:59:39.850. Z planem w pamięci podręcznej zatrzymałem sesję zdarzeń rozszerzonych:

ZMIANA SESJI ZDARZENIA [StatsUpdate_QueryPerf]ON SERVERSTATE =STOP;

Następnie dodałem około 47 milionów wierszy danych do tabeli za pomocą tego skryptu, znacznie powyżej progu niezbędnego do unieważnienia aktualnych statystyk. Po dodaniu danych zweryfikowałem liczbę wierszy w tabeli:


Big_SalesOrderHeader CI:po załadowaniu danych

Zanim ponownie uruchomiłem procedurę składowaną, sprawdziłem pamięć podręczną planu, aby upewnić się, że nic się nie zmieniło, i zweryfikowałem, że statystyki nie zostały jeszcze zaktualizowane. Pamiętaj, nawet jeśli statystyki zostały w tym momencie unieważnione, nie zostaną zaktualizowane, dopóki nie zostanie wykonane zapytanie korzystające ze statystyk (dla odniesienia:Zrozumienie, kiedy statystyki zostaną automatycznie zaktualizowane). W ostatnim kroku ponownie uruchomiłem sesję Extended Events, a następnie wielokrotnie uruchomiłem procedurę składowaną. Po tych wykonaniach ponownie sprawdziłem pamięć podręczną planu:


Pamięć podręczna planów:po załadowaniu danych

Liczba_wykonań wynosi ponownie 8, a jeśli spojrzymy na czas_tworzenia planu, zobaczymy, że zmienił się na 08-04-2014 19:32:52.913. Jeśli sprawdzimy plan, zobaczymy, że jest taki sam, mimo że plan został ponownie skompilowany:


Plan zapytań dla procedury składowanej przy użyciu Eksploratora planów SQL Sentry

Analiza danych wyjściowych zdarzeń rozszerzonych

Wziąłem pierwszy plik Extended Events – przed załadowaniem danych – i otworzyłem go w SSMS, a następnie zastosowałem filtr, tak aby wyświetlane były tylko instrukcje z procedury składowanej:


Wyniki rozszerzonego zdarzenia:po pierwszym wykonaniu SP

Widać, że istnieje osiem (8) wykonań procedury składowanej, a czasy trwania zapytań różnią się nieznacznie.

Wziąłem drugi plik Extended Events – po załadowaniu danych – otworzyłem go SSMS i ponownie przefiltrowałem, tak aby były wymienione tylko oświadczenia z procedury składowanej, a także zdarzenia auto_stats:


Wyjście zdarzeń rozszerzonych:wykonanie SP po załadowaniu danych

Dane wyjściowe są obcinane, ponieważ nie są potrzebne do wyświetlenia głównego wyniku. Wpisy podświetlone na niebiesko reprezentują pierwsze wykonanie procedury składowanej i należy zauważyć, że istnieje wiele kroków — aktualizacja statystyk jest częścią wykonania. Instrukcja SELECT zostanie uruchomiona (attach_activity_id.seq =3), a następnie zostaną wykonane aktualizacje statystyk. W naszym przykładzie faktycznie mamy aktualizacje trzech statystyk. Po zakończeniu ostatniej aktualizacji (attach_activity_id.seq =11) rozpoczyna się i kończy procedura składowana (attach_activity_id.seq =13 i attach_activity_id.seq =14). Co ciekawe, istnieje drugie zdarzenie sp_statement_starting dla procedury składowanej (przypuszczalnie pierwsze jest pomijane), więc całkowity czas trwania procedury składowanej jest obliczany bez aktualizacja statystyk.

W tym scenariuszu natychmiastowa aktualizacja statystyk — to znaczy po wykonaniu zapytania korzystającego z nieprawidłowych statystyk — powoduje, że zapytanie działa dłużej, mimo że czas trwania zapytania na podstawie zdarzenia sp_statement_completed jest nadal krótszy niż 14000. nie przynosi żadnych korzyści dla wydajności zapytań, ponieważ plan jest dokładnie taki sam przed aktualizacją statystyk i po niej. W tym scenariuszu plan zapytania i czas wykonywania nie zmieniają się po dodaniu większej ilości danych do tabeli, więc aktualizacja statystyk tylko utrudnia jej wydajność. Zobaczmy teraz, co się stanie, gdy włączymy opcję asynchronicznej automatycznej aktualizacji statystyk.

Test, wersja 2

Zaczynamy od przywrócenia kopii zapasowej, którą zrobiłem przed rozpoczęciem pierwszego testu. Odtworzyłem procedurę składowaną, a następnie zmieniłem opcję bazy danych, aby aktualizować statystyki asynchronicznie:

UŻYJ [master];BAZA DANYCH GOALTER [AdventureWorks2012_Big] WŁĄCZ AUTO_UPDATE_STATISTICS_ASYNC Z NO_WAITGO

Rozpocząłem sesję zdarzeń rozszerzonych i ponownie wielokrotnie wykonałem procedurę składowaną, używając różnych identyfikatorów CustomerID:

 ALTER EVENT SESSION [StatsUpdate_QueryPerf]ON SERVERSTATE =START;GO EXEC Sales.usp_GetCustomerStats11331, '2012-08-01 00:00:00.000', '2012-08-31 23:59:59.997'GOEXEC Sales.usp_GetCustomerStats11330, '2013-01-01 00:00:00.000', '2013-01-31 23:59:59,997'GOEXEC Sales.usp_GetCustomerStats11506, '2012-11-01 00:00:00.000', '2012-11-30 23 :59:59.997'Sprzedaż GOEXEC.usp_GetCustomerStats17061, '2013-01-01 00:00:00.000', '2013-01-31 23:59:59,997'GOEXEC Sales.usp_GetCustomerStats11711, '2013-03-01 00:00:00.000', '2013-03-31 23:59:59.997'GOEXEC Sales.usp_GetCustomerStats15131, '2013-02-01 00:00:00.000', '2013-02-28 23:59:59.997'GOEXEC Sales.usp_GetCustomerStats29837, '2012-10-01 00:00:00.000', '2012-10-31 23:59:59,997'GOEXEC Sales.usp_GetCustomerStats15750, '2013-03-01 00:00:00.000', '2013-03-31 23 :59:59,997 START

Zweryfikowałem liczbę wykonań i plan, odpytując pamięć podręczną procedur:


Pamięć podręczna planów:na początku, test 2


Plan zapytań dla procedury składowanej przy użyciu Eksploratora planów SQL Sentry

Na potrzeby tego testu plan został utworzony w dniu 2014-04-08 21:15:55.490. Zatrzymałem sesję Extended Events i ponownie dodałem około 47 milionów wierszy danych do tabeli, używając tego samego zapytania co poprzednio.

Po dodaniu danych sprawdziłem pamięć podręczną planu, aby upewnić się, że nic się nie zmieniło, i zweryfikowałem, że statystyki nie zostały jeszcze zaktualizowane. W końcu ponownie uruchomiłem sesję zdarzeń rozszerzonych, a następnie uruchomiłem procedurę składowaną jeszcze osiem razy. Ostatni wgląd do pamięci podręcznej planu pokazał liczbę_wykonań o 16 i czas utworzenia 2014-04-08 21:15:55.490. Execution_count i create_time pokazują, że statystyki nie zostały zaktualizowane, ponieważ plan nie został jeszcze wyczyszczony z pamięci podręcznej (gdyby tak było, mielibyśmy później czas utworzenia i licznik_wykonania równy 8).


Pamięć podręczna planów:po załadowaniu danych, test 2

Jeśli otworzymy dane wyjściowe zdarzeń rozszerzonych po załadowaniu danych w programie SSMS i ponownie przefiltrujemy, aby zobaczyć tylko instrukcje z procedury składowanej, a także zdarzenia auto_stats, znajdziemy to (zauważ, że dane wyjściowe są podzielone na dwa zrzuty ekranu):


Wyjście zdarzeń rozszerzonych:Test 2, wykonanie SP po załadowaniu danych, część I


Wyjście zdarzeń rozszerzonych:Test 2, wykonanie SP po załadowaniu danych, część II

Zdarzenia wykonania pierwszego wywołania procedury składowanej są podświetlone na niebiesko — zaczynają się od 2014-04-08 21:54:14.9480607 i występuje siedem (7) zdarzeń. Zwróć uwagę, że istnieją trzy (3) zdarzenia auto_stats, ale żadne z nich nie zostało ukończone, jak widzieliśmy, gdy opcja Asynchroniczna aktualizacja statystyk była wyłączona. Zauważysz, że automatyczna aktualizacja dla jednej ze statystyk zaczyna się niemal natychmiast (2014-04-08 21:54:14.9481288), a obok tych trzech zdarzeń znajduje się czerwony tekst „Stat Update #1”. Ta aktualizacja statystyk kończy się 2014-04-08 21:54:16.5392219, niecałe dwie sekundy po rozpoczęciu, ale po zakończeniu wszystkich innych procedur. To dlatego liczba_wykonań z sys.dm_exec_query_stats pokazuje 16. Na wyjściu XE widzimy, że inne aktualizacje statystyk zostały następnie zakończone (Aktualizacja Statystyczna #2 i Aktualizacja Statystyczna #3). Wszystkie aktualizacje są asynchroniczne z wykonaniem początkowej procedury składowanej.

Podsumowanie

Jak widać, automatyczne aktualizacje statystyk mogą potencjalnie negatywnie wpłynąć na wydajność zapytań. Stopień wpływu będzie zależał od ilości danych, które należy odczytać, aby zaktualizować statystyki, oraz od zasobów systemowych. W niektórych przypadkach wydajność zapytań wzrasta tylko o milisekundy i najprawdopodobniej jest niezauważalna dla użytkowników. Innym razem czas trwania może znacznie wzrosnąć, co z kolei wpływa na wrażenia użytkownika końcowego. W przypadku, gdy plan zapytań nie zmienia się po aktualizacji statystyk, warto rozważyć włączenie opcji asynchronicznej automatycznej aktualizacji statystyk, aby złagodzić wpływ na wydajność zapytań.


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

  2. Indeks klastrowy i nieklastrowy:wyjaśniono 7 najważniejszych punktów

  3. Jak wygenerować dane testowe DB

  4. Operator SQL IN dla początkujących

  5. Wprowadzenie do Hadoop i Big Data