Sqlserver
 sql >> Baza danych >  >> RDS >> Sqlserver

SQL Server 2016:sys.dm_exec_function_stats

W SQL Server 2016 CTP 2.1 pojawił się jeden nowy obiekt, który pojawił się po CTP 2.0:sys.dm_exec_function_stats. Ma to na celu zapewnienie podobnej funkcjonalności do sys.dm_exec_procedure_stats, sys.dm_exec_query_stats i sys.dm_exec_trigger_stats. Dzięki temu możliwe jest teraz śledzenie zagregowanych metryk czasu wykonywania dla funkcji zdefiniowanych przez użytkownika.

A może?

Przynajmniej w CTP 2.1 mogłem tutaj wyprowadzić tylko jakiekolwiek znaczące metryki dla zwykłych funkcji skalarnych – nic nie zostało zarejestrowane dla funkcji TVF wbudowanych lub wielowyrazowych. Nie dziwią mnie funkcje wbudowane, ponieważ i tak są one w zasadzie rozszerzane przed wykonaniem. Ale ponieważ wielowypowiedziowe programy TVF często powodują problemy z wydajnością, miałem nadzieję, że również się pojawią. Nadal pojawiają się w sys.dm_exec_query_stats, więc nadal możesz stamtąd czerpać ich metryki wydajności, ale może być trudno przeprowadzać agregacje, gdy naprawdę masz wiele instrukcji, które wykonują pewną część pracy – nic nie jest dla ciebie skumulowane.

Rzućmy okiem na to, jak to się rozgrywa. Załóżmy, że mamy prostą tabelę zawierającą 100 000 wierszy:

WYBIERZ TOP (100000) o1.[object_id], o1.create_date DO dbo.src Z sys.all_objects JAK o1 POŁĄCZENIE KRZYŻOWE sys.all_objects JAK o2 ZAMÓW WEDŁUG o1.[object_id];GOCREATE SKLASTROWANY INDEKS x NA dbo.src ([object_id]);GO-- zapełnij cacheSELECT [object_id], create_date FROM dbo.src;

Chciałem porównać, co się dzieje, gdy badamy skalarne funkcje UDF, wieloinstrukcyjne funkcje z wartościami przechowywanymi w tabeli i wbudowane funkcje z wartościami w tabeli oraz jak widzimy, jaka praca została wykonana w każdym przypadku. Najpierw wyobraź sobie coś trywialnego, co możemy zrobić w SELECT klauzulę, ale możemy chcieć oddzielić ją od siebie, jak formatowanie daty jako ciągu:

CREATE PROCEDURE dbo.p_dt_Standard @dt_ CHAR(10) =NULLASBEGIN SET NOCOUNT ON; SELECT @dt_ =CONVERT(CHAR(10), data_utworzenia, 120) FROM dbo.src ORDER BY [identyfikator_obiektu];ENDGO

(Przypisuję dane wyjściowe do zmiennej, która wymusza przeskanowanie całej tabeli, ale zapobiega wpływowi wysiłków SSMS na wykorzystanie i renderowanie danych wyjściowych przez metryki wydajności. Dziękuję za przypomnienie, Mikael Eriksson.)

Wiele razy zobaczysz, jak ludzie umieszczają tę konwersję w funkcji, która może być skalarna lub TVF, na przykład:

CREATE FUNCTION dbo.dt_Inline(@dt_ DATETIME)RETURNS TABLEAS RETURN (SELECT dt_ =CONVERT(CHAR(10), @dt_, 120));GO CREATE FUNCTION dbo.dt_Multi(@dt_ DATETIME)RETURNS @t TABLE( dt_ ZNAK(10))ASBEGIN INSERT @t(dt_) SELECT CONVERT(ZNAK(10), @dt_, 120); RETURN;ENDGO CREATE FUNCTION dbo.dt_Scalar(@dt_ DATETIME)RETURNS CHAR(10)ASBEGIN RETURN (SELECT CONVERT(CHAR(10), @dt_, 120));ENDGO

Stworzyłem otoki procedur wokół tych funkcji w następujący sposób:

CREATE PROCEDURE dbo.p_dt_Inline @dt_ CHAR(10) =NULLASBEGIN SET NOCOUNT ON; SELECT @dt_ =dt.dt_ FROM dbo.src AS o CROSS APPLY dbo.dt_Inline(o.create_date) AS dt ORDER BY o.[object_id];ENDGO CREATE PROCEDURE dbo.p_dt_Multi @dt_ CHAR(10) =NULLAS SET NOCOUNT; SELECT @dt_ =dt.dt_ FROM dbo.src CROSS APPLY dbo.dt_Multi(create_date) AS dt ORDER BY [object_id];ENDGO CREATE PROCEDURE dbo.p_dt_Scalar @dt_ CHAR(10) =NULLASBEGIN SET NOCOUNT ON; SELECT @dt_ =dt =dbo.dt_Scalar(create_date) FROM dbo.src ORDER BY [object_id];ENDGO

(I nie, dt_ Konwencja, którą widzisz, nie jest niczym nowym, myślę, że jest dobrym pomysłem, był to po prostu najprostszy sposób, w jaki mogłem odizolować wszystkie te zapytania w DMV od wszystkiego, co zostało zebrane. Ułatwiło to również dodawanie przyrostków, aby łatwo odróżnić zapytanie wewnątrz procedury składowanej od wersji ad hoc).

Następnie utworzyłem tabelę #temp do przechowywania czasów i powtórzyłem ten proces (zarówno dwukrotne wykonanie procedury składowanej, jak i dwukrotne wykonanie treści procedury jako izolowanego zapytania ad hoc oraz śledzenie czasu każdego z nich):

CREATE TABLE #t( ID INT IDENTITY(1,1), q VARCHAR(32), s DATETIME2, e DATETIME2);GO INSERT #t(q,s) VALUES('p Standard',SYSDATETIME());GO EXEC dbo.p_dt_Standard;GO 2 UPDATE #t SET e =SYSDATETIME() WHERE ID =1;GO INSERT #t(q,s) VALUES('standard ad hoc',SYSDATETIME());GO DECLARE @dt_st CHAR (10); SELECT @dt_st =CONVERT(CHAR(10), create_date, 120) FROM dbo.src ORDER BY [identyfikator_obiektu];GO 2 UPDATE #t SET e =SYSDATETIME() WHERE ID =2;GO-- powtórz dla inline, multi i wersje skalarne

Następnie wykonałem kilka zapytań diagnostycznych i oto wyniki:

sys.dm_exec_function_stats

SELECT nazwa =NAZWA_OBIEKTU(id_obiektu), liczba_wykonań, czas_milisekund =całkowity_upływ czasu/1000FROM sys.dm_exec_function_statsWHERE database_id =DB_ID()ORDER BY name;

Wyniki:

nazwa liczba_wykonań czas_milisekundy--------------------------- -----------------dt_Scalar 400000 1116 

To nie jest literówka; tylko skalarny UDF pokazuje jakąkolwiek obecność w nowym DMV.

sys.dm_exec_procedure_stats

 SELECT nazwa =NAZWA_OBIEKTU (identyfikator_obiektu), liczba_wykonań, czas_milisekund =całkowity_upływ czasu/1000FROM sys.dm_exec_procedure_statsWHERE database_id =DB_ID()ORDER BY name;

Wyniki:

nazwa liczba_wykonań czas_milisekundy--------------- --------------- ----- -p_dt_Inline 2 74p_dt_Multi 2 269p_dt_Scalar 2 1063p_dt_Standard 2 75

Nie jest to zaskakujący wynik:użycie funkcji skalarnej prowadzi do spadku wydajności o rząd wielkości, podczas gdy wielowyrazowy TVF był tylko około 4 razy gorszy. W wielu testach funkcja wbudowana była zawsze tak szybka lub o milisekundę lub dwie szybsze niż żadna funkcja.

sys.dm_exec_query_stats

zapytanie SELECT =SUBSTRING([tekst],s,e), liczba_wykonań, czas_milisekundyFROM(SELECT t.[tekst], s =s.statement_start_offset/2 + 1, e =COALESCE(NULLIF(s.statement_end_offset,-1 ),8000)/2, s.execution_count, time_milliseconds =s.total_elapsed_time/1000 Z sys.dm_exec_query_stats JAK s ZEWNĘTRZNE ZASTOSOWANIE sys.dm_exec_sql_text(s.[sql_handle]) JAK GDZIE JEŚLI [N'%d] _]%' ) JAK x;

Skrócone wyniki, ponownie uporządkowane ręcznie:

zapytanie (obcięte) liczba_wykonań czas_milisekundy--------------------------------------------- --------------------------- --------------- -------- ----------- p Standardowy:SELECT @dt_ =CONVERT(ZNAK(10), data_utworzenia, 120) ... 2 75-- ad hoc Standardowy:SELECT @dt_st =CONVERT(ZNAK(10) , data_utworzenia, 120) ... 2 72 -- p Inline:SELECT @dt_ =dt.dt_ FROM dbo.src AS o CROSS APPLY dbo.dt_Inline... 2 74-- ad hoc Inline:SELECT @dt_in =dt. dt_ FROM dbo.src AS o CROSS APPLY dbo.dt_Inline... 2 72 -- all Multi:INSERT @t(dt_) SELECT CONVERT(CHAR(10), @dt_, 120); 184 5-- p Multi:SELECT @dt_ =dt.dt_ FROM dbo.src CROSS APPLY dbo.dt_Multi... 2 270-- ad hoc Multi:SELECT @dt_m =dt.dt_ FROM dbo.src AS o CROSS APPLY dbo .dt_Multi... 2 257 -- wszystkie skalar:RETURN (SELECT CONVERT(CHAR(10), @dt_, 120)); 400000 581-- p Scalar:SELECT @dt_ =dbo.dt_Scalar(create_date)... 2 986-- ad hoc Scalar:SELECT @dt_sc =dbo.dt_Scalar(create_date)... 2 902

Ważną rzeczą, na którą należy zwrócić uwagę, jest to, że czas w milisekundach dla instrukcji INSERT w wielowyrazowej instrukcji TVF i instrukcji RETURN w funkcji skalarnej są również uwzględniane w poszczególnych SELECTach, więc nie ma sensu po prostu zsumować wszystkich czasy.

Czasy ręczne

I wreszcie czasy z tabeli #temp:

Zapytanie SELECT =q, czas_milisekundy =DATEDIFF(milisekunda, s, e) FROM #t ORDER BY ID;

Wyniki:

czas zapytania_milisekundy--------------- -----------------p Standardowy 107ad hoc Standardowy 78p Wbudowany 80ad hoc Wbudowany 78p Multi 351ad hoc Multi 263p Skalar 992ad hoc Skalar 907

Dodatkowe interesujące wyniki:otoczka procedury zawsze wiązała się z pewnym obciążeniem, chociaż jego znaczenie może być naprawdę subiektywne.

Podsumowanie

Chodziło mi o to, aby dzisiaj pokazać nowy DMV w akcji i poprawnie ustawić oczekiwania – niektóre wskaźniki wydajności funkcji nadal będą mylące, a niektóre nadal będą w ogóle niedostępne (lub przynajmniej będą bardzo żmudne, aby samemu złożyć je w całość ).

Myślę, że ten nowy DMV obejmuje jeden z największych elementów monitorowania zapytań, których wcześniej brakowało SQL Server:funkcje skalarne są czasami niewidocznymi zabójcami wydajności, ponieważ jedynym niezawodnym sposobem identyfikacji ich użycia było przeanalizowanie tekstu zapytania, co jest daleka od niezawodnego. Nieważne, że nie pozwoli to na wyizolowanie ich wpływu na wydajność, lub że musiałbyś wiedzieć, że szukasz skalarnych UDF w tekście zapytania.

Załącznik

Załączam skrypt:DMExecFunctionStats.zip

Ponadto, od CTP1, oto zestaw kolumn:

database_id object_id type type_desc
sql_handle plan_handle cached_time last_execution_time execution_count
total_worker_time last_worker_time min_worker_time max_worker_time
total_physical_reads last_physical_reads min_physical_reads max_physical_reads
total_logical_writes last_logical_writes min_logical_writes max_logical_writes
total_logical_reads last_logical_reads min_logical_reads max_logical_reads
total_elapsed_time last_elapsed_time min_elapsed_time max_elapsed_time

Kolumny aktualnie w sys.dm_exec_function_stats


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Jak uniknąć pojedynczego cytatu w programie SQL Server?

  2. Jak naprawić błąd „nazwa profilu jest nieprawidłowa” podczas aktualizowania profilu poczty bazy danych w programie SQL Server (T-SQL)

  3. Symulowanie funkcji group_concat MySQL w Microsoft SQL Server 2005?

  4. TransactSQL do uruchomienia innego skryptu TransactSQL

  5. Dlaczego moje połączenie ODBC kończy się niepowodzeniem podczas uruchamiania ładowania SSIS w programie Visual Studio, ale nie podczas uruchamiania tego samego pakietu przy użyciu narzędzia Wykonaj pakiet