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

Wdrażanie wskaźnika wydajności programu SQL Server dla zapytań, procedur składowanych i wyzwalaczy

Przedmowa

Wcześniej czy później administrator bazy danych chciałby mieć wskaźnik wydajności dla zapytań SQL Server. Jak wszyscy wiemy, uruchomienie Profilera przez 24 godziny doprowadzi do znacznego obciążenia systemu i dlatego nie można go uznać za optymalne rozwiązanie dla baz danych używanych w trybie 24/7.

Jak więc możemy wykryć stan zapytań SQL Server? Jak możemy uruchomić śledzenie wykrytych problemów związanych z zapytaniami bez udziału człowieka?

W tym artykule przedstawię implementację wskaźnika wydajności SQL Server dla zapytań, procedur składowanych i wyzwalaczy, a także jego wykorzystanie do uruchomienia śledzenia.

Rozwiązanie

Przede wszystkim przyjrzyjmy się ogólnemu podejściu do implementacji wskaźnika wydajności dla zapytań, procedur składowanych i wyzwalaczy:

  1. Tworzenie wymaganych tabel do zbierania i analizy informacji.
  2. Tworzenie widoku do zbierania informacji.
  3. Tworzenie przechowywanych procedur do zbierania informacji.
  4. Tworzenie widoku do wyświetlania informacji.

A teraz rozważmy implementację:

1. Tworzenie wymaganych tabel do zbierania i analizy informacji.

1.1. W przypadku zapytań:

UŻYJ [DATABASE_NAME]GOSET ANSI_NULLS ONGOSET QUOTED_IDENTIFIER ONGOSET ANSI_PADDING ONGOCREATE TABLE [srv].[SQL_StatementExecStat]( [ID] [bigint] IDENTITY(1,1) NOT NULL, [InsertDate] [datetimeH] NULL, binarny](8) NULL, [ExecutionCount] [bigint] NULL, [TotalWorkerTime] [bigint] NULL, [StatementText] [nvarchar](max) NULL, [TotalElapsedTime] [bigint] NULL, CONSTRAINT [PK_SQL_StatementExecStat] PODSTAWOWY KLASTER KLUCZY ( [ID] ASC) Z (PAD_INDEX =OFF, STATISTICS_NORECOMPUTE =OFF, IGNORE_DUP_KEY =OFF, ALLOW_ROW_LOCKS =ON, ALLOW_PAGE_LOCKS =ON) ON [PRIMARY]) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]GOSET ANSI_pre>GOSET ON
 1.2. Dla procedur składowanych:

 USE [DATABASE_NAME]GOSET ANSI_NULLS ONGOSET QUOTED_IDENTIFIER ONGOCREATE TABLE [srv].[SQL_ProcedureExecStat]([ID] [bigint] IDENTITY(1,1) NOT NULL, [InsertDate] [datetime] NULL, [database_id] [int] NULL, [object_id] [int] NULL, [ExecutionCount] [bigint] NULL, [TotalWorkerTime] [bigint] NULL, [TotalElapsedTime] [bigint] NULL, [TotalPhysicalReads] [bigint] NULL, [TotalLogicalReads] [bigint] NULL, [TotalLogicalWrites] [bigint] NULL, CONSTRAINT [PK_SQL_ProcedureExecStat] KLUCZ PODSTAWOWY CLUSTERED ( [ID] ASC) Z (PAD_INDEX =OFF, STATISTICS_NORECOMPUTE =OFF, IGNORE_DUP_KEY =OFF, ALLOW_LOCKROW_LOCKSAGE =ON_, ALLOWARY ON_, ALLS PRIMARY]IDŹ

1.3. Dla wyzwalaczy:

UŻYJ [DATABASE_NAME]GOSET ANSI_NULLS ONGOSET QUOTED_IDENTIFIER ONGOCREATE TABLE [srv].[SQL_TriggerExecStat]([ID] [bigint] IDENTITY (1,1) NOT NULL, [InsertDate] [datetime] NULL, [database_id] [int] NULL, [object_id] [int] NULL, [ExecutionCount] [bigint] NULL, [TotalWorkerTime] [bigint] NULL, [TotalElapsedTime] [bigint] NULL) ON [PRIMARY]GO

2. Stworzenie widoku do zbierania informacji (tutaj możemy wstawić filtry w celu pozbycia się nieistotnych informacji (np. zapytań i procedur z wyzwalaczami replikacji itp.).
2.1. Dla zapytań:

UŻYJ [DATABASE_NAME]GOSET ANSI_NULLS ONGOSET QUOTED_IDENTIFIER ONGOCREATE widok [srv].[vStatementExecInfo] jak z informacjami jako (SELECT query_stats.query_hash AS QueryHash, SUM(query_stats.total_worker_time )_(countCPU. query_stats.execution_count ) AS ExecutionCount, SUM(query_stats.total_worker_time ) AS TotalWorkerTime, MIN(query_stats.statement_text ) AS StatementText, MIN(query_stats.min_worker_time ) AS MinWorkerTime, MAX(zapytanie_maks.czas_odczytu_stat. TotalPhysicalReads, MIN(query_stats.min_physical_reads) AS MinPhysicalReads, MAX(query_stats.max_physical_reads) AS MaxPhysicalReads, SUM(query_stats.total_physical_reads) / SUM(query_stats.execution_count) AS AvgPhysicalReads, SUM(query_stats.total_logical_writes) AS TotalLogicalWrites, MIN(query_stats.min_logical_writes) AS MinLogicalWrites, MAX(query_stats.max_logical_writes ) AS MaxLogicalWrites, SUM(query_stats. total_logical_reads ) AS TotalLogicalReads, MIN(query_stats.min_logical_reads ) AS MinLogicalReads, MAX(query_stats.max_logical_reads ) AS MaxLogicalReads, SUM(query_stats.total_logical_reads ) / SUM(query_stats. query_stats.min_elapsed_time ) AS MinElapsedTime, MAX(query_stats.max_elapsed_time ) AS MaxElapsedTime, SUM(query_stats.total_elapsed_time ) / SUM(query_stats.execution_count) AS Av gElapsedTime, MIN(query_stats.creation_time ) AS MinCreationTime, MAX(query_stats.last_execution_time ) AS LastExecuteTimeFROM (SELECT QS.query_hash ,QS.total_worker_time ,QS.execution_count ,QS. .total_physical_reads ,QS.total_logical_writes ,QS.min_logical_writes ,QS.max_logical_writes ,QS.min_logical_reads ,QS.max_logical_reads ,QS.total_logical_reads ,QS.min_elapsed_timetal_elapsed.QS.min_elapsed_timetal_. ,QS.creation_time ,QS.last_execution_time ,SUBSTRING(ST.text, (QS.statement_start_offset/2) + 1, ((instrukcja CASE_end_offset WHEN -1 THEN DATALENGTH(ST.text) ELSE QS.statement_end_offset END - QS.statement_start_offset) 2) + 1) AS statement_text Z sys.dm_exec_query_stats JAK QS CROSS APPLY sys.dm_exec_sql_text(QS.sql_handle) as ST) as query_statsGDZIE liczba_wykonań> 1i last_execution_time>=dateadd query(godzina,-3,getdate_hashGROUP.BY) )select QueryHash, AvgCPU_Time, ExecutionCount, TotalWorkerTime, StatementText, MinWorkerTime, MaxWorkerTime, TotalPhysicalReads, MinPhysicalReads, MaxPhysicalReads, AvgPhysicalReads, TotalLogicalWrites, MinLogicalWrites, MaxLogicalWrites, AvgLogicalReads,MaxLogicalReads,Total AvgLogicalReads, TotalElapsedTime, MinElapsedTime, MaxElapsedTime, AvgElapsedTime, MinCreationTime, LastExecuteTimefrom infoGO

W tym przypadku używane są następujące zapytania systemowe:sys.dm_exec_query_stats i sys.dm_exec_sql_text.
2.2. Dla procedur składowanych:

UŻYJ [DATABASE_NAME]GOSET ANSI_NULLS ONGOSET QUOTED_IDENTIFIER ONGOCREATE widok [srv].[vProcedureExecInfo] jak z info as (SELECT procedure_stats.database_id AS database_id, procedure_stats.object_id AS object_id, MIN(procedure_stature_stature,S. .total_worker_time ) / SUM(procedure_stats.execution_count) AS AvgCPU_Time, SUM(procedure_stats.execution_count ) AS ExecutionCount, SUM(procedure_stats.total_worker_time ) AS TotalWorkerTime, MIN_Textproc. (procedure_stats.max_worker_time ) AS MaxWorkerTime, SUM(procedure_stats.total_physical_reads) AS TotalPhysicalReads, MIN(procedure_stats.min_ph ysical_reads ) AS MinPhysicalReads, MAX(procedure_stats.max_physical_reads ) AS MaxPhysicalReads, SUM(procedure_stats.total_physical_reads) / SUM(procedure_stats.execution_count) AS AvgPhysicalReads, SUMlogical_swrite_procedure_Logical. procedure_stats.max_logical_writes ) AS MaxLogicalWrites, SUM(procedure_stats.total_logical_writes) / SUM(procedure_stats.execution_count) AS AvgLogicalWrites, SUM(procedure_stats.total_logical_reads ) ASlogical_min_stats. SUM(procedure_stats.total_logical_reads) / SUM(procedure_stats.execution_count) AS AvgLogicalReads, SUM(procedure_stats.total_elap sed_time ) AS TotalElapsedTime, MIN(procedure_stats.min_elapsed_time ) AS MinElapsedTime, MAX(procedure_stats.max_elapsed_time ) AS MaxElapsedTime, SUM(procedure_stats.total_elapsed_time ) / SUM_state(proc.czas_proc.executured ASMIN_time(proc. procedure_stats.last_execution_time ) AS LastExecuteTimeFROM (SELECT QS.database_id ,QS.object_id ,QS.type ,QS.total_worker_time ,QS.execution_count ,QS.min_worker_time ,QS.max_worker_time ,QS.fizyczny.QSread. total_logical_writes , QS.min_logical_writes ,QS.max_logical_writes ,QS.min_logical_reads ,QS.max_logical_reads ,QS.total_logical_reads ,QS.min_elapsed_time ,QS.max_elapsed_time ,QS.total_elapsed_Qcached_time. QS KRZYŻ ZASTOSUJ sys.dm_exec_sql_text(QS.sql_handle) jako ST) as procedure_statsGDZIE liczba_wykonań> 1 i czas_ostatniego_wykonania>=dateadd(godzina,-3,getdate())GROUP WG database_id,object_id)select database_id, object_id, type, Avg, Avg TotalWorkerTime, ProcedureText, MinWorkerTime, MaxWorkerTime, TotalPhysicalReads, MinPhysicalReads, MaxPhysicalRead s, AvgPhysicalReads, TotalLogicalWrites, MinLogicalWrites, MaxLogicalWrites, AvgLogicalWrites, TotalLogicalReads, MinLogicalReads, MaxLogicalReads, AvgLogicalReads, TotalElapsedTime, MinElapsedTime, MaxElapsedTime, AvgElapsedTimefrom, MinGOCached> 

W tym przypadku używane są następujące zapytania systemowe:sys.dm_exec_Procedure_stats i sys.dm_exec_sql_text.

2.3. Dla wyzwalaczy:

UŻYJ [DATABASE_NAME]GOSET ANSI_NULLS ONGOSET QUOTED_IDENTIFIER ONGOCREATE widok [srv].[vTriggerExecInfo] jak z info as (SELECT procedure_stats.database_id AS database_id, procedure_stats.object_id AS object_id, MIN(procedure(procedure_types_stature)S. .total_worker_time ) / SUM(procedure_stats.execution_count) AS AvgCPU_Time, SUM(procedure_stats.execution_count ) AS ExecutionCount, SUM(procedure_stats.total_worker_time ) AS TotalWorkerTime, MIN_Textproc. (procedure_stats.max_worker_time ) AS MaxWorkerTime, SUM(procedure_stats.total_physical_reads) AS TotalPhysicalReads, MIN(procedure_stats.min_phys ical_reads ) AS MinPhysicalReads, MAX(procedure_stats.max_physical_reads ) AS MaxPhysicalReads, SUM(procedure_stats.total_physical_reads) / SUM(procedure_stats.execution_count) AS AvgPhysicalReads, SUM(procedure_write_min. procedure_stats.max_logical_writes ) AS MaxLogicalWrites, SUM(procedure_stats.total_logical_writes) / SUM(procedure_stats.execution_count) AS AvgLogicalWrites, SUM(procedure_stats.total_logical_reads ) ASlogical_min_stats. SUM(procedure_stats.total_logical_reads ) / SUM(procedure_stats.execution_count) AS AvgLogicalReads, SUM(procedure_stats.total_elapse d_time ) AS TotalElapsedTime, MIN(procedure_stats.min_elapsed_time ) AS MinElapsedTime, MAX(procedure_stats.max_elapsed_time ) AS MaxElapsedTime, SUM(procedure_stats.total_elapsed_time ) / SUM(procedure_stats. procedure_stats.last_execution_time ) AS LastExecuteTimeFROM (SELECT QS.database_id ,QS.object_id ,QS.type ,QS.total_worker_time ,QS.execution_count ,QS.min_worker_time ,QS.max_worker_time ,QS.fizyczny.QSread. total_logical_writes ,QS .min_logical_writes ,QS.max_logical_writes ,QS.min_logical_reads ,QS.max_logical_reads ,QS.total_logical_reads ,QS.min_elapsed_time ,QS.max_elapsed_time ,QS.total_elapsed_time_cachion_st.time_st. KRZYŻ ZASTOSUJ sys.dm_exec_sql_text(QS.sql_handle) jako ST) jako procedure_statsGDZIE liczba_wykonań> 1 i czas_ostatniego_wykonania>=dateadd(godzina,-3,getdate())GRUPA WEDŁUG database_id,object_id)select database_id, object_id, type, AvgeCcution , ProcedureText, MinWorkerTime, MaxWorkerTime, TotalPhysicalReads, MinPhysicalReads, MaxPhysicalReads, AvgPhysicalReads, TotalLogicalWrites, MinLogicalWrites, MaxLogicalWrites, AvgLogicalWrites, TotalLogicalReads, MinLogicalReads, MaxLogicalReads, AvgLogicalReads, TotalElapsedTime, MinElapsedTime, MaxElapsedTime, AvgElapsedTime, MinExecuteedTime, informacje 

W tym przypadku używane są następujące zapytania systemowe:sys.dm_exec_trigger_stats i sys.dm_exec_sql_text.

3. Tworzenie procedur składowanych do gromadzenia informacji.

3.1. W przypadku zapytań:

USE [DATABASE_NAME]GOSET ANSI_NULLS ONGOSET QUOTED_IDENTIFIER ONGOCREATE PROCEDURE [srv].[InsertForSQL_StatementExecStat] @koef dziesiętny(12,2)=0.0 –współczynnik zbierania --jest wybierany w sposób eksperymentalny dla dokładniejszego zbierania, --w większości przypadkach możemy wpisać 0.0, --jeśli częstotliwość uruchamiania kolekcji nie przekracza 5 minut. --Dokładność obliczeń zależy od częstotliwości zbierania i współczynnika zbierania. --Im częstsze uruchamianie kolekcji, tym mniejszy wpływ ma współczynnik zbierania.ASBEGIN SET NOCOUNT ON; zadeklaruj @AvgCPU_Time bigint ,@MaxAvgCPU_Time bigint ,@AvgTotalWorkerTime bigint ,@MaxTotalWorkerTime bigint ,@AvgAvgElapsedTime bigint ,@MaxAvgElapsedTime bigint ,@AvgTotalElapsedTime bigint ,@AvgTotalElapsedTime bigint ,@MaxAvG_TalElapsedTime =AvgTotalWorkerTime =AVG(TotalWorkerTime), @MaxTotalWorkerTime =max(TotalWorkerTime), @AvgAvgElapsedTime =AVG(AvgElapsedTime), @MaxAvgElapsedTime =max(AvgElapsedTime), @AvgTotalElapsedTime =AVG(TotalElapsedTime)(TotalElapsedTime); insert into srv.SQL_StatementExecStat ( [InsertDate] ,[QueryHash] ,[ExecutionCount] ,[TotalWorkerTime] ,[StatementText] ,[TotalElapsedTime]) select getdate() ,[QueryHash] ,[ExecutionCount] ,[Text][SterateTime] ,[TotalElapsedTime] z srv.vStatementExecInfo gdzie(AvgCPU_Time> @AvgCPU_Time + @koef * (@MaxAvgCPU_Time - @AvgCPU_Time)) lub (TotalWorkerTime> @AvgTotalWorkerTime + @koef * (@TimeEvGAlapse - @AWorkerA) ortal - + @koef * (@MaxAvgElapsedTime - @AvgAvgElapsedTime)) lub (TotalElapsedTime> @AvgTotalElapsedTime + @koef * (@MaxTotalElapsedTime - @AvgTotalElapsedTime));ENDGO

3.2. Dla procedur składowanych:

USE [DATABASE_NAME]GOSET ANSI_NULLS ONGOSET QUOTED_IDENTIFIER ONGOCREATE PROCEDURE [srv].[InsertForProcedureExecStat] @koef dziesiętny(12,2)=0.0 --współczynnik zbierania --jest wybierany eksperymentalnie w celu dokładniejszego zbierania, --in w większości przypadków możemy wpisać 0.0, -- jeśli częstotliwość uruchamiania kolekcji nie przekracza 5 minut. --Dokładność obliczeń zależy od częstotliwości zbierania i współczynnika zbierania. --Im częstsze uruchamianie kolekcji, tym mniejszy wpływ ma współczynnik zbierania.ASBEGIN SET NOCOUNT ON; zadeklaruj @AvgCPU_Time bigint ,@MaxAvgCPU_Time bigint ,@AvgTotalWorkerTime bigint ,@MaxTotalWorkerTime bigint ,@AvgAvgElapsedTime bigint ,@MaxAvgElapsedTime bigint ,@AvgTotalElapsedTime bigint ,@Max bigint; select @AvgCPU_Time =AVG(AvgCPU_Time), @MaxAvgCPU_Time =max(AvgCPU_Time), @AvgTotalWorkerTime =AVG(TotalWorkerTime), @MaxTotalWorkerTime =max(TotalWorkerTime), @AvgAvgElapsedTime =AVG(TimeAvgElapsed), @AvgElapsed =AVG(TimeAvgElapsed) AvgTotalElapsedTime =AVG(TotalElapsedTime), @MaxTotalElapsedTime =max(TotalElapsedTime) z srv.vProcedureExecInfo; wstawiamy do srv.SQL_ProcedureExecStat ( [InsertDate] ,database_id ,object_id ,[ExecutionCount] ,[TotalWorkerTime] ,[TotalElapsedTime] ,[TotalPhysicalReads] ,[TotalLogicalReads] ,[TotalLogicaltimeddate_cution(()]) ,[TotalWorkerTime] ,[TotalElapsedTime] ,[TotalPhysicalReads] ,[TotalLogicalReads] ,[TotalLogicalWrites] z srv.vProcedureExecInfo gdzie (AvgCPU_Time> @AvgCPU_Time + @koef * (@MaxAvgAvgCPU_Tal -CzasCPU_C koef * (@MaxTotalWorkerTime - @AvgTotalWorkerTime)) lub (AvgElapsedTime> @AvgAvgElapsedTime + @koef * (@MaxAvgElapsedTime - @AvgAvgElapsedTime)) lub (TotalElapsedTime> @AvgTotalElapsedTime + @koef * apsedTime - @AvgTotalElapsedTime));ENDGO

3.3. Dla wyzwalaczy:

USE [DATABASE_NAME]GOSET ANSI_NULLS ONGOSET QUOTED_IDENTIFIER ONGOCREATE PROCEDURE [srv].[InsertForTriggerExecStat] @koef decimal(12,2)=0.0 --współczynnik zbierania --jest wybierany w sposób eksperymentalny w celu dokładniejszego zbierania, --in w większości przypadków możemy podać 0.0, --jeśli częstotliwość uruchamiania kolekcji nie przekracza 5 minut. --Dokładność obliczeń zależy od częstotliwości zbierania i współczynnika zbierania. --Im częstsze uruchamianie kolekcji, tym mniejszy wpływ ma współczynnik zbierania.ASBEGIN SET NOCOUNT ON; zadeklaruj @AvgCPU_Time bigint ,@MaxAvgCPU_Time bigint ,@AvgTotalWorkerTime bigint ,@MaxTotalWorkerTime bigint ,@AvgAvgElapsedTime bigint ,@MaxAvgElapsedTime bigint ,@AvgTotalElapsedTime bigint ,@AvgTotalElapsedTime bigint ,@MaxAvG_TalElapsedTime =AvgTotalWorkerTime =AVG(TotalWorkerTime), @MaxTotalWorkerTime =max(TotalWorkerTime), @AvgAvgElapsedTime =AVG(AvgElapsedTime), @MaxAvgElapsedTime =max(AvgElapsedTime), @AvgTotalElapsedTime =AVG(TotalElapsedTime)(TotalElapsedTime); wstaw do srv.SQL_TriggerExecStat ( [InsertDate] ,database_id ,object_id ,[ExecutionCount] ,[TotalWorkerTime] ,[TotalElapsedTime]) select getdate() ,database_id ,object_id ,[ExecutionCount]Timer. gdzie(AvgCPU_Time> @AvgCPU_Time + @koef * (@MaxAvgCPU_Time - @AvgCPU_Time)) lub (TotalWorkerTime> @AvgTotalWorkerTime + @koef * (@MaxTotalWorkerTime - @AvgTotalWorkerTime)) lub (AvgElapsedTime -vgElapsedTime> @koef *A @AvgAvgElapsedTime)) lub (TotalElapsedTime> @AvgTotalElapsedTime + @koef * (@MaxTotalElapsedTime - @AvgTotalElapsedTime));ENDGO

4. Stworzenie widoku dla wyjścia informacji.

4.1. W przypadku zapytań:

UŻYJ [DATABASE_NAME]GOSET ANSI_NULLS ONGOSET QUOTED_IDENTIFIER ONGOCREATE WIDOK [srv].[vStatementExecTotalInfo]asselect ExecutionCount jako Num ,TotalWorkerTime jako TotalWorkerTime ,TotalElapsedTime as TotalElapsedTime,vg.PUCde(As) convert(decimal(8,2),AvgElapsedTime/1000000.) as AvgElapsedSec ,... ,QueryHash ,StatementText z [SRV].[srv].[vStatementExecInfo];GO

4.2. Dla procedur składowanych:

UŻYJ [DATABASE_NAME]GOSET ANSI_NULLS ONGOSET QUOTED_IDENTIFIER ONGOCREATE VIEW [srv].[vProcedureExecTotalInfo]as wybierz ExecutionCount jako Num ,TotalWorkerTime jako TotalWorkerTime ,TotalElapsedTime jako TotalElapsedTime_AvPU. ,convert(dziesiętny(8,2),Śr.czas trwania/1000000.) as Śr.ElapsedSec ,... ,identyfikator_danych ,id_obiektu ,nazwa_bazy(id_bazy_danych) as Nazwa_DB ,NAZWA_SCHEMATU_OBIEKTU(id_obiektu, id_bazy_danych) as Nazwa_schematu ,id_obiektu,nazwa_bazy_danych( z [SRV].[srv].[vProcedureExecInfo];GO

4.3. Widoki dla wyzwalaczy są tworzone w podobny sposób (jeśli jest to wymagane). Jeśli chodzi o mnie, nie potrzebuję wyzwalaczy śledzenia, ponieważ jeśli są jakieś problemy z wyzwalaczami, pokaże je wykonanie procedur składowanych i zapytań.

Następujące dwa parametry mają kluczowe znaczenie dla zaimplementowanych widoków:

  1. AvgWorkerSec — czas wykonania zapytania w sekundach.
  2. AvgElapsedSec — czas oczekiwania lub czas oczekiwania+AvgWorkerSec.

Jeśli chodzi o wyniki poglądów, ważna jest następująca równość:

AvgWorkerSec=Avg ElapsedSec

  1. AvgWorkerSec>AvgElapsedSec – tutaj coś mocno obciąża procesor w momencie wykonywania zapytania (jak się okazało, trwało skanowanie oprogramowania antywirusowego; może to być również wina planu równoległego).
  2. Śr.robocza s

Jeśli przestrzegane jest AvgWorkerSec=AvgElapsedSec, długi czas wykonania jest powiązany z samym zapytaniem i czasem jego wykonania.

Jakie jest kryterium wykonania długiego zapytania?

Nie ma absolutnej odpowiedzi na to pytanie. Zależy to od tego, co robi zapytanie, gdzie i jak jest używane itp.

Mam następującą ocenę zapytań ad hoc i procedur składowanych:

  1. Do 0,5 – dobre dla procedur składowanych, bez problemów (bez czekania na wykonanie).
  2. Do 0,1 – dobre dla zapytań, bez problemów (bez czekania na wykonanie).
  3. 0,5 — 1,0 – złe dla procedur składowanych, występują problemy (nie ma czekania na wykonanie, które jest widoczne dla użytkownika, ale nadal istnieją i wymagają rozwiązania).
  4. 0,1 — 0,5 — złe dla zapytań, występują problemy (nie ma czekania na wykonanie, które jest widoczne dla użytkownika, ale nadal istnieją i wymagają rozwiązania).
  5. Ponad 1.0 – złe dla procedur składowanych, występują problemy (istnieje duża szansa, że ​​są oczekiwania widoczne dla użytkowników, problem wymaga natychmiastowego rozwiązania).
  6. Powyżej 0,5 – źle dla zapytań, są problemy (istnieje duża szansa, że ​​są oczekiwania widoczne dla użytkowników, problem wymaga natychmiastowego rozwiązania).

W przypadku zapytań innych niż ad hoc (przesyłanie danych, ładowanie danych) powyższa ocena jest dobierana indywidualnie. Zwykle znacznie przekracza oceny dla zapytań ad hoc i procedur składowanych.

Jeśli całe oprogramowanie działa za pośrednictwem procedur składowanych, można śledzić tylko procedury składowane, ponieważ praca zapytań zawsze wpływa na pracę procedur składowanych. Dlatego postawmy na analizę wykonania procedur składowanych.

Stwórzmy system do zbierania informacji o najcięższych procedurach składowanych do późniejszej analizy i uruchamiania automatycznego śledzenia, zgodnie z następującym algorytmem:

1. Utworzenie tabeli do przechowywania informacji:

UŻYJ [DATABASE_NAME]GOSET ANSI_NULLS ONGOSET QUOTED_IDENTIFIER ONGOCREATE TABLE [srv].[SQL_TopProcedureExecStat]( [Row_GUID] [unikalny identyfikator] NOT NULL, [SERVER] [nvarchar](255) NOT NULL [int]NULL] , [OBJECT_ID] [int] NOT NULL, [ExecutionCount] [bigint] NOT NULL, [TotalWorkerTime] [bigint] NULL, [TotalElapsedTime] [bigint] NULL, [Func] [dziesiętny](8, 2) NULL, [AvgWorkerSec ] [dziesiętny](8, 2) NULL, [AvgElapsedSec] [dziesiętny](8, 2) NULL, [DB_NAME] [nvarchar](255) NULL, [SCHEMA_NAME] [nvarchar](255) NULL, [OBJECT_NAME] [ nvarchar](255) NULL, [InsertUTCDate] [datetime] NOT NULL, [TotalPhysicalReads] [bigint] NULL, [TotalLogicalReads] [bigint] NULL, [TotalLogicalWrites] [bigint] NULL, [AvgPhysicalReads] [bigint]NULL, [sA ] [bigint] NULL, [AvgLogicalWrites] [bigint] NULL, [NazwaKategorii] [nvarchar](255) NULL, CONSTRAINT [PK_ SQL_TopProcedureExecStat] KLUCZ PODSTAWOWY ([Row_GUID] ASC) Z (PAD_INDEX =WYŁ, STATISTICS_NORECOMPUTE =WYŁ, IGNORE_DUP_KEY =WYŁ, ALLOW_ROW_LOCKS =WŁ, ALLOW_PAGE_LOCKS =WŁ) WŁ. ] DODAJ OGRANICZENIE [DF_SQL_TopProcedureExecStat_Row_GUID] DOMYŚLNE (newid()) DLA [Row_GUID]TABELI GOALTER [srv].[SQL_TopProcedureExecStat] DODAJ OGRANICZENIE [DF_SQL_TopProcedureExecStat_ProSERVER_GOALTER_TABELA]DFAULT (@Stat_ProSERVER_GOAL_TABLIC) OGRANICZENIE [DF_SQL_TopProcedureExecStat_InsertUTCDate] DOMYŚLNE (getutcdate()) DLA [InsertUTCDate]GO

2. Utworzenie procedury składowanej do zbierania informacji:

USE [DATABASE_NAME]GOSET ANSI_NULLS ONGOSET QUOTED_IDENTIFIER ONGOCREATE PROCEDURE [srv].[InsertTopProcedureExecStat] @top tinyint=24 – liczba dni do przechowywania rekordów ,@NazwaKategorii nvarchar(255)='ec'AvgCOUNTerS NA; INSERT INTO [srv].[SQL_TopProcedureExecStat] ([DB_ID] ,[OBJECT_ID] ,[ExecutionCount] ,[TotalWorkerTime] ,[TotalElapsedTime] ,[AvgWorkerSec] ,[AvgElapsedSec] ,[SCH_NAME] InsertUTCDate , NazwaKategorii , TotalPhysicalReads , TotalLogicalReads ,TotalLogicalWrites ,AvgPhysicalReads ,AvgLogicalReads ,AvgLogicalWrites) select top(@top) [identyfikator bazy danych] ,[object_id] ,[Liczba],Czas pracy,[Czas pracy],[Upływ czasu] [DB_NAME] ,[SCHEMA_NAME] ,[PROCEDURE_NAME] ,InsertUTCDate ,C ategoryName ,TotalPhysicalReads ,TotalLogicalReads ,TotalLogicalWrites ,AvgPhysicalReads ,AvgLogicalReads ,AvgLogicalWrites from( select [database_id] ,[object_id] ,[Num] ,[TotalWorkerTime] ,[TotalElapsedTime] ,[AvgWorkerSec] ,[AvgElapsedSec] ,[DB_NAME] ,[SCHEMA_NAME] ,[PROCEDURE_NAME] ,getUTCDate() as InsertUTCDate ,@CategoryName as CategoryName ,TotalPhysicalReads ,TotalLogicalReads ,TotalLogicalWrites ,AvgPhysicalReads ,AvgLogicalReads ,AvgL ogicalWrites FROM [srv].[vProcedureExecTotalInfoHour] ) as t order by case @CategoryName when 'TotalWorkerTime' then TotalWorkerTime when 'TotalElapsedTime' then TotalElapsedTime when 'AvgWorkerSec' then AvgWorkerSec when 'AvgElapsedSec' then AvgElapsedSec when 'TotalPhysicalReads' then TotalPhysicalReads when 'TotalLogicalReads' then TotalLogicalReads when 'TotalLogicalWrites' then TotalLogicalWrites when 'AvgPhysicalReads' then AvgPhysicalReads when 'AvgLogicalReads' then AvgLogicalReads when 'AvgLogicalWrites' then AvgLogicalWrites end desc; declare @count int=(select count(*) from [srv].[SQL_TopProcedureExecStat] where [email protected]); declare @diff [email protected]@top;;with tbl_del as( select Row_GUID from [srv].[SQL_TopProcedureExecStat] where InsertUTCDate0) begin;with tbl_del as( select top(@diff) Row_GUID from [srv].[SQL_TopProcedureExecStat] where [email protected] order by case @CategoryName when 'TotalWorkerTime' then TotalWorkerTime when 'TotalElapsedTime' then TotalElapsedTime when 'AvgWorkerSec' then AvgWorkerSec when 'AvgElapsedSec' then AvgElapsedSec when 'TotalPhysicalReads' then TotalP hysicalReads when 'TotalLogicalReads' then TotalLogicalReads when 'TotalLogicalWrites' then TotalLogicalWrites when 'AvgPhysicalReads' then AvgPhysicalReads when 'AvgLogicalReads' then AvgLogicalReads when 'AvgLogicalWrites' then AvgLogicalWrites end ) delete from [srv].[SQL_TopProcedureExecStat] where Row_GUID in (select Row_GUID from tbl_del); end declare @DB_ID int declare @OBJECT_ID int declare @top1 int =3 declare @diff1 int declare @count1 int -- deletion of more than @top1 times repeats of the specific procedure select top (1) @count1 =tp.num ,@DB_ID =tp.DB_ID ,@OBJECT_ID =tp.OBJECT_ID from (select count(*) as num, DB_ID, OBJECT_ID from [srv].[SQL_TopProcedureExecStat] where [email protected] group by DB_ID, OBJECT_ID) as tp order by tp.num desc; set @diff1 =@count1 - @top1; if(@diff1)> 0 begin;with tbl_del as( select top(@diff1) Row_GUID from [srv].[SQL_TopProcedureExecStat] where DB_ID =@DB_ID and OBJECT_ID =@OBJECT_ID and [email protected] order by case @CategoryName when 'TotalWorkerTime' then TotalWorkerTime when 'TotalElapsedTime' then TotalElapsedTime when 'AvgWorkerSec' then AvgWorkerSec when 'AvgElapsedSec' then AvgElapsedSec when 'TotalPhysicalReads' then TotalPhysicalReads when 'TotalLogicalReads' then Tot alLogicalReads when 'TotalLogicalWrites' then TotalLogicalWrites when 'AvgPhysicalReads' then AvgPhysicalReads when 'AvgLogicalReads' then AvgLogicalReads when 'AvgLogicalWrites' then AvgLogicalWrites end ) delete from [srv].[SQL_TopProcedureExecStat] where Row_GUID in (select Row_GUID from tbl_del); end -- deletion of more than 1 repeats of the AvgWorkerSec parameter for the specific procedure if @CategoryName ='AvgWorkerSec' begin declare @AvgWorkerSec decimal(8,2) select top (1) @count1 =tp.num ,@DB_ID =tp.DB_ID ,@OBJECT_ID =tp.OBJECT_ID ,@AvgWorkerSec =tp.AvgWorkerSec from (select count(*) as num, DB_ID, OBJECT_ID, AvgWorkerSec from [srv].[SQL_TopProcedureExecStat] where [email protected] group by DB_ID, OBJECT_ID,AvgWorkerSec) as tp order by tp.num desc; set @diff1 =@count1 - 1; if(@diff1)> 0 begin;with tbl_del as( select top(@diff1) Row_GUID from [srv].[SQL_TopProcedureExecStat] where DB_ID =@DB_ID and OBJECT_ID =@OBJECT_ID and [email protected] and AvgWorkerSec =@AvgWorkerSec order by InsertUTCDate desc ) delete from [srv].[SQL_TopProcedureExecStat] where Row_GUID in (select Row_GUID from tbl_del); end end if @CategoryName ='AvgElapsedSec' begin declare @AvgElapsedSec decimal(8,2) select top (1) @count1 =tp.num ,@DB_ID =tp.DB_ID ,@OBJECT_ID =tp.OBJECT_ID ,@AvgElapsedSec =tp.AvgElapsedSec from (select count(*) as num, DB_ID, OBJECT_ID, AvgElapsedSec from [srv].[SQL_TopProcedureExecStat] where [email protected] group by DB_ID, OBJECT_ID,AvgElapsedSec) as tp order by tp.num desc; set @diff1 =@count1 - 1; if(@diff1)> 0 begin;with tbl_del as( select top(@diff1) Row_GUID from [srv].[SQL_TopProcedureExecStat] where DB_ID =@DB_ID and OBJECT_ID =@OBJECT_ID and [email protected] and AvgElapsedSec =@AvgElapsedSec order by InsertUTCDate desc ) delete from [srv].[SQL_TopProcedureExecStat] where Row_GUID in (select Row_GUID from tbl_del); end endENDGO

It is better to run this stored procedure immediately after collecting information about the stored procedures (we can set up a task in Agent for running it every 5-10 minutes for queries, stored procedures and triggers):

exec [srv].[InsertForSQL_StatementExecStat]; --collecting information about executed queriesexec [srv].[InsertForTriggerExecStat]; --collecting information about executed triggersexec [srv].[InsertForProcedureExecStat]; --collecting information about executed stored procedures--collecting information about the most heavy executed stored procedures, according to the criteriaexec [srv].[InsertTopProcedureExecStat] @[email protected], @CategoryName='AvgWorkerSec';exec [srv].[InsertTopProcedureExecStat] @[email protected], @CategoryName='AvgElapsedSec'

3. Running trace (every 5-10 minutes with the help of the Agent tasks, preferably right after collecting information):

USE [DATABASE_NAME];go--coefficient of transition value of indicatordeclare @koef_red numeric(8,3)=1.3; --if there are records with the indicator greater than or equal to the --preset indicator coefficient if(exists( SELECT top(1) 1 FROM [srv].[SQL_TopProcedureExecStat] where CategoryName='AvgElapsedSec' or CategoryName='AvgWorkerSec' group by CategoryName having avg([AvgElapsedSec])>[email protected]_red or avg([AvgWorkerSec])>[email protected]_red)) begin --running autorace exec .[srv].[AutoTrace]; end

The auto-trace stored procedure is implemented on an individual basis. Na przykład:

USE [DATABASE_NAME]GOSET ANSI_NULLS ONGOSET QUOTED_IDENTIFIER ONGOCREATE PROCEDURE [srv].[AutoTrace] @maxfilesize bigint=200 --maximum file size in Mb ,@run_minutes int=60 --tracing length in minutes ,@file_patch nvarchar(255)=N'Path to directory' --directory for trace file ,@file_name nvarchar(255)=N'Profiler' --file name ,@res_msg nvarchar(255)=NULL output --result in the form of messagesASBEGIN SET NOCOUNT ON; declare @rc int; declare @TraceID int; if(@run_minutes>=1200) set @run_minutes=1200; --no longer than 20 hours! declare @finish_dt datetime=DateAdd(minute,@run_minutes,GetDate()); --execution end time --end of trace file declare @finish_dt_inc nvarchar(255)=N'_'+cast(YEAR(@finish_dt) as nvarchar(255))+'_'+cast(MONTH(@finish_dt) as nvarchar(255))+'_'+cast(DAY(@finish_dt) as nvarchar(255)); declare @File nvarchar(255)[email protected]@[email protected]_dt_inc; --full name of the trace file DECLARE @result bit; DECLARE @msgerrors nvarchar(255); DECLARE @oldDT datetime; --Getting the last date and time if(object_id('DATABASE_NAME.dbo.TraceTable')<>0) begin select @oldDT=max(StartTime) from DATABASE_NAME.dbo.TraceTable where StartTime is not null; end --select @oldDT; --If the last date and time is not specified or it is less than time of trace ending,trace is run. Otherwise, the trace was executed on this date. if(@oldDT is null or @oldDT=10) set @run_delay_hour_str=cast(@run_delay_hour as nvarchar(255)); --select @run_delay_hour, @run_delay_hour_str; --adding missing nulls for string representation of minutes if(@run_delay_minute=0) set @run_delay_minute_str='00'; else if(@run_delay_minute<10) set @run_delay_minute_str='0'+cast(@run_delay_minute as nvarchar(255)); else if(@run_delay_minute>=10) set @run_delay_minute_str=cast(@run_delay_minute as nvarchar(255)); --select @run_delay_minute, @run_delay_minute_str; --the hours:minutes string representation for the wait declare @run_delay_str nvarchar(255)[email protected]_delay_hour_str+':'[email protected]_delay_minute_str; --wait WAITFOR DELAY @run_delay_str; --select @run_delay_str; --deletion of the trace table, if it exists if(object_id('DATABASE_NAME.dbo.TraceTable')<>0) begin drop table DATABASE_NAME.dbo.TraceTable; end --creation and filling of the trace table from the trace file SELECT * INTO DATABASE_NAME.dbo.TraceTable FROM ::fn_trace_gettable(@File+'.trc', default); --adding extension to the full file set @[email protected]+'.trc'; --here, we need to insert code to delete the trace file declare @str_title nvarchar(max)='There was auto trace on the server'[email protected]@servername, @str_pred_mess nvarchar(max)='На '[email protected]@servername+'The auto trace has been run on the server. You can view the result in the Database_Name.dbo.TraceTable table; --here, we can send the auto trace run notification to administrator end --returning the result set @res_msg=N'ErrorCode='+cast(@rc as nvarchar(255))+'\r\n'+coalesce(@msgerrors, ''); endENDGO

For more information on setting trace, refer to How to:Create a Trace (Transact-SQL).

Wniosek

In this article, we considered an example of implementation of a system for collecting information about the state of a database, that does not load the system. In case of problem detection, this system runs the preset trace and saves results into a table. This approach can be extended to several servers. In this case, we need to collect information from all servers for subsequent sending of information to administrators.

It is also important to remember about deletion of old data from the used tables. It is quite sufficient to store data within a month or two weeks.

Also read:

Implementing a Common MS SQL Server Performance Indicator

References

  • sys.dm_exec_trigger_stats
  • sys.dm_exec_procedure_stats
  • sys.dm_exec_query_stats
  • sys.dm_exec_sql_text
  • How to:Create a Trace (Transact-SQL)

  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Funkcja SQL Server ROUND():do czego służy i dlaczego powinno cię to obchodzić?

  2. Tworzenie wyzwalaczy audytu w SQL Server

  3. Jak wykonać GROUP BY na aliasowanej kolumnie w MS-SQL Server?

  4. Tabela z wieloma wyrażeniami Wartości funkcji w porównaniu z tabelą inline Wartości funkcji

  5. Co to jest CZAS STATYSTYKI w SQL Server?