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'GOZweryfikował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 SentryWidział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 danychZanim 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 danychLiczba_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 SentryAnaliza 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 SPWidać, ż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 danychDane 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_WAITGORozpoczął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 STARTZweryfikował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 SentryNa 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 2Jeś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ęść IIZdarzenia 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ń.