sql >> Database teknologi >  >> RDS >> Sqlserver

Implementering af SQL Server Performance Indicator for forespørgsler, lagrede procedurer og triggere

Forord

Før eller siden vil en DB-administrator gerne have en ydeevneindikator for SQL Server-forespørgsler. Som vi alle ved, vil det at køre Profiler i 24 timer føre til en betydelig systembelastning, og det kan derfor ikke betragtes som en optimal løsning til databaser, der bruges i 24/7-tilstand.

Så hvordan kan vi opdage tilstanden af ​​SQL Server-forespørgsler? Hvordan kan vi køre spor efter opdagede forespørgselsrelaterede problemer uden menneskelig input?

I denne artikel vil jeg give en implementering af SQL Server-ydeevneindikatoren for forespørgsler, lagrede procedurer og triggere, såvel som dens brug til sporingskørslen.

Løsning

Først og fremmest, lad os tage et kig på den generelle tilgang til implementering af ydeevneindikatoren for forespørgsler, lagrede procedurer og triggere:

  1. Oprettelse af nødvendige tabeller til informationsindsamling og analyse.
  2. Oprettelse af en visning til indsamling af oplysninger.
  3. Oprettelse af lagrede procedurer til informationsindsamling.
  4. Oprettelse af en visning til informationsoutput.

Og lad os nu overveje implementeringen:

1. Oprettelse af nødvendige tabeller til informationsindsamling og analyse.

1.1. For forespørgsler:

BRUG [DATABASE_NAME]GOSET ANSI_NULLS ONGOSET QUOTED_IDENTIFIER ONGOSET ANSI_PADDING ONGOCREATE TABLE [srv].[SQL_StatementExecStat]( [ID] [bigint] IDENTITET(1,1) IKKE NULL] [Dato] NULL, [Indsæt]Dato, NULL, [Indsæt] binær](8) NULL, [ExecutionCount] [bigint] NULL, [TotalWorkerTime] [bigint] NULL, [StatementText] [nvarchar](max) NULL, [TotalElapsedTime] [bigint] NULL, CONSTRAINT [PK_SQL_PRISTERMARYED KEYStat] [ID] ASC)MED (PAD_INDEX =FRA, STATISTICS_NORECOMPUTE =FRA, IGNORE_DUP_KEY =FRA, ALLOW_ROW_LOCKS =TIL, ALLOW_PAGE_LOCKS =TIL) PÅ [PRIMÆR]) PÅ [PRIMÆR] TEXTIMAGE_PÅ [PRIMARY] TEKSTIMAGE]_PÅ [PRÆMÆR] 

1.2. For lagrede procedurer:

BRUG [DATABASE_NAME]GOSET ANSI_NULLS ONGOSET QUOTED_IDENTIFIER ONGOCREATE TABLE [srv].[SQL_ProcedureExecStat]( [ID] [bigint] IDENTITET(1,1) IKKE NULL, [Indsæt Dato], [dato]_ NULL] NULL, [object_id] [int] NULL, [ExecutionCount] [bigint] NULL, [TotalWorkerTime] [bigint] NULL, [TotalElapsedTime] [bigint] NULL, [TotalPhysicalReads] [bigint] NULL, [TotalLogicalReads] [TotalLogicalWrites] [bigint] NULL, BEGRÆNSNING [PK_SQL_ProcedureExecStat] PRIMÆR NØGLE KLUSTERET ( [ID] ASC) MED (PAD_INDEX =FRA, STATISTICS_NORECOMPUTE =FRA, IGNORE_DUP_KEY =FRA_TILLADS_TILLADES_) PRIMÆR]GO

1.3. Til udløsere:

BRUG [DATABASE_NAME]GOSET ANSI_NULLS ONGOSET QUOTED_IDENTIFIER ONGOCREATE TABEL [srv].[SQL_TriggerExecStat]( [ID] [bigint] IDENTITET(1,1) IKKE NULL, [Indsæt Dato], [dato]_ NULL] NULL, [object_id] [int] NULL, [ExecutionCount] [bigint] NULL, [TotalWorkerTime] [bigint] NULL, [TotalElapsedTime] [bigint] NULL) PÅ [PRIMARY]GO

2. Oprettelse af en visning til informationsindsamling (her kan vi indsætte filtre til at slippe af med den irrelevante information (f.eks. forespørgsler og procedurer med replikeringsudløsere osv.).
2.1. For forespørgsler:

BRUG [DATABASE_NAME]GOSET ANSI_NULLS ONGOSET QUOTED_IDENTIFIER ONGOCREATE visning [srv].[vStatementExecInfo] som med info som (SELECT query_stats.query_hash AS QueryHash, SUM(query_stats.total_worker_time)SUM_worker_time(execution_worker_time)SUM. 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._max_forespørgsel_til_forespørgselstid, MAX_forespørgsel_til_forespørgsel_kal. 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_logical_tæller_,SUMg. 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.stat_forespørgsel_forespørgselstid(forespørgsel_forespørgsel_udfør_forespørgsel) 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.min_worker_time,Qs_worker_time,Qs_worker_time,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_time_elaps,QS.min_elaps_elaps. ,QS.creation_time ,QS.last_execution_time ,SUBSTRING(ST.text, (QS.statement_start_offset/2) + 1, ((CASE statement_end_offset WHEN -1 THEN DATALENGTH(ST.text) ELSE QS.statement_end_offset END - QS.offset)_start_ 2) + 1) AS statement_text FROM sys.dm_exec_query_stats AS QS CROSS APPLY sys.dm_exec_sql_text(QS.sql_handle) as ST) as query_statsWHERE execution_count> 1and last_execution_time>=dateadd,query_bystat querysha. )vælg QueryHash, AvgCPU_Time, ExecutionCount, TotalWorkerTime, StatementText, MinWorkerTime, MaxWorkerTime, TotalPhysicalReads, MinPhysicalReads, MaxPhysicalReads, AvgPhysicalReads, TotalLogicalWrites, MinLogicalWritical, MinLogicalWritical, MaxReadLogical, MinLogicalWritical, MinLogicalWritical, MaxReadLogical AvgLogicalReads, TotalElapsedTime, MinElapsedTime, MaxElapsedTime, AvgElapsedTime, MinCreationTime, LastExecuteTimefrom infoGO

Her bruges følgende systemforespørgsler:sys.dm_exec_query_stats og sys.dm_exec_sql_text.
2.2. For lagrede procedurer:

BRUG [DATABASE_NAME]GOSET ANSI_NULLS ONGOSET QUOTED_IDENTIFIER ONGOCREATE visning [srv].[vProcedureExecInfo] som med info som (SELECT procedure_stats.database_id AS database_id, procedure_stats.object_id AS object_id, SUM(procedure_stats,sUM()_procedure_stats, .total_worker_time ) / SUM(procedure_stats.execution_count) AS Gns.CPU_Time, SUM(procedure_stats.execution_count ) AS ExecutionCount, SUM(procedure_stats.total_worker_time ) AS TotalWorkerTime, MIN(procedure_stats.ProcedureText ) AS_ProcedureText(s)min. (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, SUM(procedure_logritical_totals 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 ) AS TotalLogicalReads, MINsRead_max_procedures, MINs_procedurs_max. 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(procedure_stats.execution_counts. 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.min_physical .QS_read . 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 ,QS.cached_time ,QS.last_execution_time ,ST.text as Proceduretext FROM sys.dm_exec_Procedure_stats AS QS CROSS APPLY sys.dm_exec_sql_text(QS.sql_handle) as ST) as procedure_statsWHERE execution_count> 1and last_execution_time>=dateadd(hour,-3,getdate())GROUP BY database_id,object_id)select databaseCount_ID,Excuid TotalWorkerTime, ProcedureText, MinWorkerTime, MaxWorkerTime, TotalPhysicalReads, MinPhysicalReads, MaxPhysicalRead s, AvgPhysicalReads, TotalLogicalWrites, MinLogicalWrites, MaxLogicalWrites, AvgLogicalWrites, TotalLogicalReads, MinLogicalReads, MaxLogicalReads, AvgLogicalReads, TotalElapsedTime, MinElapsedTime, MinElapsedTime, MaxElapeCutfTime, MinElapsCutfTime, MinElapsCutf. 

Her bruges følgende systemforespørgsler:sys.dm_exec_Procedure_stats og sys.dm_exec_sql_text.

2.3. Til udløsere:

BRUG [DATABASE_NAME]GOSET ANSI_NULLS ONGOSET QUOTED_IDENTIFIER ONGOCREATE visning [srv].[vTriggerExecInfo] som med info som (SELECT procedure_stats.database_id AS database_id, procedure_stats.object_id AS object_id, SUM(procedure_stats,sUM(procedure_stats,sUM() .total_worker_time ) / SUM(procedure_stats.execution_count) AS Gns.CPU_Time, SUM(procedure_stats.execution_count ) AS ExecutionCount, SUM(procedure_stats.total_worker_time ) AS TotalWorkerTime, MIN(procedure_stats.ProcedureText ) AS_ProcedureText(s)min. (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_stats_totalsMIN)Procedure_stats_total_MAX_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 ) AS TotalLogicalReads, MINsRead_max_procedures, MINs_procedurs_max. SUM(procedure_stats.total_logical_reads ) / SUM(procedure_stats.execution_count) AS AvgLogicalReads, SUM(procedure_stats.total_elapse d_tid ) 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.Gennemsnitlig_omløbstidspunkt,AgElapsedTime MAX,Min. 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.min_physical .QS_read . 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 ,QS.cached_time ,QS.last_execution_time ,ST.text as Proceduretext FROM sys.dm_exec_trigger_stats AS QS CROSS APPLY sys.dm_exec_sql_text(QS.sql_handle) as ST) as procedure_statsWHERE execution_count> 1and last_execution_time>=dateadd(time,-3,getdate())GROUP BY database_id,object_id)select database_id, objectecukerW , ProcedureText, MinWorkerTime, MaxWorkerTime, TotalPhysicalReads, MinPhysicalReads, MaxPhysicalReads, AvgPhysicalReads, TotalLogicalWrites, MinLogicalWrites, MaxLogicalWrites, AvgLogicalWrites, TotalLogicalReads, MinLogicalReads, MaxLogicalReads, AvgLogicalReads, TotalElapsedTime, MinElapsedTime, MaxElapsedTime, MaxElapsedTime, AvgElapsedTime,MaxElapsedTime,Genv. 

Her bruges følgende systemforespørgsler:sys.dm_exec_trigger_stats og sys.dm_exec_sql_text.

3. Oprettelse af lagrede procedurer for informationsindsamling.

3.1. For forespørgsler:

BRUG [DATABASE_NAME]GOSET ANSI_NULLS ONGOSET QUOTED_IDENTIFIER ONGOCREATE PROCEDURE [srv].[InsertForSQL_StatementExecStat] @koef decimal(12,2)=0.0 –opsamlingskoefficient --udvælges på en eksperimentel måde for mere nøjagtige indsamlinger. tilfælde, kan vi sætte 0,0, --hvis hyppigheden af ​​indsamlingen, der kører, ikke overstiger 5 minutter. --Nøjagtighed af beregning afhænger af indsamlingsfrekvensen og indsamlingskoefficienten. --Jo hyppigere indsamlingen kører, jo mindre indflydelse har indsamlingskoefficienten. Deklaration @Avgcpu_Time Bigint,@maxavgcpu_time bigint,@avgtotalworkertime bigint,@maxtotalorkertime bigint,@avgavgelapsedtime bigint,@maxavgelapsedtime bigint,@avgtotalelapsedtime bigint,@maxtalelapsedtime bigint SELECTI AvgTotalWorkerTime =AVG(TotalWorkerTime), @MaxTotalWorkerTime =max(TotalWorkerTime), @AvgAvgElapsedTime =AVG(AvgElapsedTime), @MaxAvgElapsedTime =max(AvgElapsedTimeTime), @AvgTotalElapsedTime.; indsæt i srv.SQL_StatementExecStat ( [InsertDate] ,[QueryHash],[ExecutionCount],[TotalWorkerTime],[StatementText],[TotalElapsedTime]) vælg getdate() ,[QueryHash] ,[ExecutionCount],[ExecutionCount]Wor] ,[TotalElapsedTime] fra srv.vStatementExecInfo where(AvgCPU_Time> @AvgCPU_Time + @koef * (@MaxAvgCPU_Time - @AvgCPU_Time)) or (TotalWorkerTime> @AvgTotalWorkerTime + @AvgTotalWorkerTime + @kAvtalWorkerTime + @kAvtalWorkerTime + @kAvgToxWorkTime>Elaped + @koef * (@MaxAvgElapsedTime - @AvgAvgElapsedTime)) eller (TotalElapsedTime> @AvgTotalElapsedTime + @koef * (@MaxTotalElapsedTime - @AvgTotalElapsedTime));ENDGO

3.2. For lagrede procedurer:

BRUG [DATABASE_NAME]GOSET ANSI_NULLS ONGOSET QUOTED_IDENTIFIER ONGOCREATE PROCEDURE [srv].[InsertForProcedureExecStat] @koef decimal(12,2)=0.0 --indsamlingskoefficient --udvælges på en eksperimentel måde, --på en mere nøjagtig indsamling i de fleste tilfælde kan vi sætte 0,0, -- hvis hyppigheden af ​​afviklingen af ​​indsamlingen ikke overstiger 5 minutter. --Nøjagtighed af beregning afhænger af indsamlingsfrekvensen og indsamlingskoefficienten. --Jo hyppigere indsamlingen kører, jo mindre indflydelse har indsamlingskoefficienten. erklær @AvgCPU_Time bigint ,@MaxAvgCPU_Time bigint ,@AvgTotalWorkerTime bigint ,@MaxTotalWorkerTime bigint ,@AvgAvgElapsedTime bigint ,@MaxAvgElapsedTime bigint ,@AvgTotalTid bigint,Elapsed@AvgTotalElapsed@AvgTotalElaps; vælg @AvgCPU_Time =AVG(AvgCPU_Time), @MaxAvgCPU_Time =max(AvgCPU_Time), @AvgTotalWorkerTime =AVG(TotalWorkerTime), @MaxTotalWorkerTime =max(TotalWorkerTime), @AvgAvgElapsedAlapsTime =ElapsedAvgMaxElapsedAx AvgTotalElapsedTime =AVG(TotalElapsedTime), @MaxTotalElapsedTime =max(TotalElapsedTime) from srv.vProcedureExecInfo; indsæt i srv.SQL_ProcedureExecStat ( [InsertDate] ,database_id ,object_id ,[ExecutionCount] ,[TotalWorkerTime],[TotalElapsedTime] ,[TotalPhysicalReads] ,[TotalLogicalReads] ,[TotalLogicalReads] ,[TotalLogicalReads] ,[TotalLogicalReads] ,[TotalLogicalReads] ,[totalt.dato]database]get_Write] ,[totalt. ,[TotalWorkerTime] ,[TotalElapsedTime] ,[TotalPhysicalReads] ,[TotalLogicalReads] ,[TotalLogicalWrites] fra srv.vProcedureExecInfo hvor(AvgCPU_Time> @MavgCPU_Time @AvgCPU_Time @xAoefkerTid @xAoefg *(WgCPU_Time @xAoefg * (CvkerTid) + @kAoeftal *(W_Tid -A) koef * (@MaxTotalWorkerTime - @AvgTotalWorkerTime)) eller (AvgElapsedTime> @AvgAvgElapsedTime + @koef * (@MaxAvgElapsedTime - @AvgAvgElapsedTime)) eller (TotalElapsedTime @MaxElapsedTime> @ElapsedgTotal(+ElapsedgTotal) apsedTime - @AvgTotalElapsedTime));ENDGO

3.3. Til udløsere:

BRUG [DATABASE_NAME]GOSET ANSI_NULLS ONGOSET QUOTED_IDENTIFIER ONGOCREATE PROCEDURE [srv].[InsertForTriggerExecStat] @koef decimal(12,2)=0.0 --indsamlingskoefficient --udvælges på en eksperimentel måde, --på en mere nøjagtig indsamling i de fleste tilfælde kan vi sætte 0,0, --hvis hyppigheden af ​​indsamlingen, der kører, ikke overstiger 5 minutter. --Nøjagtighed af beregning afhænger af indsamlingsfrekvensen og indsamlingskoefficienten. --Jo hyppigere indsamlingen kører, jo mindre indflydelse har indsamlingskoefficienten. Deklaration @Avgcpu_Time Bigint,@maxavgcpu_time bigint,@avgtotalworkertime bigint,@maxtotalorkertime bigint,@avgavgelapsedtime bigint,@maxavgelapsedtime bigint,@avgtotalelapsedtime bigint,@maxtalelapsedtime bigint SELECTI AvgTotalWorkerTime =AVG(TotalWorkerTime), @MaxTotalWorkerTime =max(TotalWorkerTime), @AvgAvgElapsedTime =AVG(AvgElapsedTime), @MaxAvgElapsedTime =maks(AvgElapsedTime=AVgElapsedTime), @AvgTotalElapsedTime fromMaxElapsElapsed; indsæt i srv.SQL_TriggerExecStat ( [InsertDate] ,database_id ,object_id ,[ExecutionCount] ,[TotalWorkerTime],[TotalElapsedTime]) vælg getdate() ,database_id ,object_id ,[ExecutionCount]orTokervsTime from[ExecutionCount]orTokervIn hvor(AvgCPU_Time> @AvgCPU_Time + @koef * (@MaxAvgCPU_Time - @AvgCPU_Time)) eller (TotalWorkerTime> @AvgTotalWorkerTime + @koef * (@MaxTotalWorkerTime - @AvgTotalWorkerTime @ElapedTime @ElapedTime @ElapedTime)) eller (Avg +Evg. @AvgAvgElapsedTime)) eller (TotalElapsedTime> @AvgTotalElapsedTime + @koef * (@MaxTotalElapsedTime - @AvgTotalElapsedTime));ENDGO

4. Oprettelse af en visning for informationsoutputtet.

4.1. For forespørgsler:

BRUG [DATABASE_NAME]GOSET ANSI_NULLS ONGOSET QUOTED_IDENTIFIER ONGOCREATE VISNING [srv].[vStatementExecTotalInfo]vælg ExecutionCount as Num ,TotalWorkerTime as TotalWorkerTime ,TotalElapsedTime as TotalElapsedTime as TotalElapsedTime as TotalElapsedTime as TotalElapsedTime as TotalElapsg0deci,conv0c0aci)(80g0C,Av0c0,000,00,00,00,00,00,00,00,00,00,00,000,000,000,000,000,000,000,000,000,000,000,000,00,00,00. convert(decimal(8,2),AvgElapsedTime/1000000.) as AvgElapsedSec ,... ,QueryHash ,StatementText from [SRV].[srv].[vStatementExecInfo];GO

4.2. For lagrede procedurer:

BRUG [DATABASE_NAME]GOSET ANSI_NULLS ONGOSET QUOTED_IDENTIFIER ONGOCREATE VIS [srv].[vProcedureExecTotalInfo]as vælg ExecutionCount as Num ,TotalWorkerTime as TotalWorkerTime ,TotalElapsedTime as TotalElapsedTime as TotalElapsedTime as TotalElapsedTime as TotalElapsedTime as TotalElapsedTime as TotalElapsedTime as TotalElapsedTime as TotalElapsedTime_asci. ,convert(decimal(8,2),AvgElapsedTime/1000000.) as AvgElapsedSec ,... ,database_id ,object_id ,db_name(database_id) as DB_Name ,OBJECT_SCHEMA_NAME(object_id, database_id) som Schema_Name_Name_ fra [SRV].[srv].[vProcedureExecInfo];GO

4.3. Visninger for triggere oprettes på en lignende måde (hvis påkrævet). Hvad mig angår, har jeg ikke brug for sporingstriggere, da hvis der er problemer med triggere, vil udførelse af lagrede procedurer og forespørgsler vise dem.

De følgende to parametre har afgørende betydning for de implementerede visninger:

  1. AvgWorkerSec — forespørgselsudførelsestid i sekunder.
  2. AvgElapsedSec — ventetid eller ventetid+AvgWorkerSec.

Hvad angår resultaterne af synspunkter, er følgende lighed vigtig:

AvgWorkerSec=AvgElapsedSec

  1. AvgWorkerSec>AvgElapsedSec – her belaster noget processoren tungt i det øjeblik, forespørgslen udføres (det viste sig, at antivirussoftwarescanningen kørte; det kan også være skyld i planen, der paralleliseres).
  2. AvgWorkerSec

Hvis AvgWorkerSec=AvgElapsedSec overholdes, er den lange udførelsestid relateret til selve forespørgslen og dens udførelsestid.

Hvad er et kriterium for den lange forespørgselsudførelse?

Der er ikke noget absolut svar på dette spørgsmål. Det afhænger af, hvad en forespørgsel gør, hvor og hvordan den bruges osv.

Jeg har følgende evaluering for ad hoc-forespørgsler og lagrede procedurer:

  1. Op til 0,5 – god til lagrede procedurer, ingen problemer (ingen udførelsesventer).
  2. Op til 0.1 – god til forespørgsler, ingen problemer (ingen udførelse venter).
  3. 0,5 – 1,0 – dårligt for lagrede procedurer, der er problemer (der er ingen udførelsesventer, der er synlige for en bruger, men de eksisterer stadig og kræver løsning).
  4. 0.1 - 0.5 - dårligt for forespørgsler, der er problemer (der er ingen udførelsesventer, der er synlige for en bruger, men de eksisterer stadig og kræver løsning).
  5. Mere end 1.0 – dårligt for lagrede procedurer, der er problemer (der er stor chance for, at der er ventetider, der er synlige for brugerne, problemet kræver øjeblikkelig løsning).
  6. Mere end 0,5 – dårligt til forespørgsler, der er problemer (der er en stor chance for, at der er ventetider, der er synlige for brugerne, problemet kræver øjeblikkelig løsning).

Hvad angår ikke-ad hoc-forespørgsler (dataupload, dataindlæsning), er ovenstående evaluering valgt på individuel basis. Normalt overgår det massivt evalueringer for ad hoc-forespørgsler og lagrede procedurer.

Hvis al software fungerer gennem de lagrede procedurer, kan du kun spore lagrede procedurer, da arbejdet med forespørgsler altid påvirker arbejdet med lagrede procedurer. Det er derfor, lad os nøjes med en analyse af udførelse af lagrede procedurer.

Lad os skabe et system til at indsamle information om de mest tunge lagrede procedurer til efterfølgende analyse og kørsel af autotrace i henhold til følgende algoritme:

1. Oprettelse af en tabel til lagring af information:

BRUG [DATABASE_NAME]GOSET ANSI_NULLS ONGOSET QUOTED_IDENTIFIER ONGOCREATE TABLE [srv].[SQL_TopProcedureExecStat]( [Row_GUID] [uniqueidentifier] IKKE NULL, [SERVER] [nvarchar_ID](255) [int] NOT NULL , [OBJECT_ID] [int] NOT NULL, [ExecutionCount] [bigint] NOT NULL, [TotalWorkerTime] [bigint] NULL, [TotalElapsedTime] [bigint] NULL, [Func] [decimal](8, 2) NULL, [AvgecWorkerS ] [decimal](8, 2) NULL, [AvgElapsedSec] [decimal](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, [Rev.RevgPhysical, NULL] [bigintadgPhysical] ] [bigint] NULL, [AvgLogicalWrites] [bigint] NULL, [CategoryName] [nvarchar](255) NULL, CONSTRAINT [PK_ Ex SQL_TopProcedureExecStat] PRIMÆR NØGLE KLUSTERET ( [Row_GUID] ASC) MED (PAD_INDEX =OFF, STATISTICS_NORECOMPUTE =OFF, IGNORE_DUP_KEY =OFF, ALLOW_ROW_LOCKS =ON, ALLOW_INDEX =OFF, STATISTICS_NORECOMPUTE =OFF, IGNORE_DUP_KEY =OFF, ALLOW_ROW_LOCKS =ON, ALLOW_INDEX =OFFs [QLec. ] TILFØJ BEGRÆNSNING [DF_SQL_TopProcedureExecStat_Row_GUID] DEFAULT (newid()) FOR [Row_GUID]MÅLTERTABEL [srv].[SQL_TopProcedureExecStat] TILFØJ BEGRÆNSNING [DF_SQL_TopProcedureExecStat@SERVER]DEVERT_ProcedureExecStat@SERVER]DEVERT_ProcedLop_SERVER]DEVER. KONSTRAINT [DF_SQL_TopProcedureExecStat_InsertUTCDate] STANDARD (getutcdate()) FOR [InsertUTCDate]GO

2. Oprettelse af en lagret procedure til indsamling af oplysninger:

BRUG [DATABASE_NAME]GOSET ANSI_NULLS ONGOSET QUOTED_IDENTIFIER ONGOCREATE PROCEDURE [srv].[InsertTopProcedureExecStat] @top tinyint=24 – antal dage til at gemme poster ,@CategoryName nvarchar(255) COUNT SET a COUNT – BEGINSÆT et COUNT – COUNT (255) PÅ; INSERT INTO [srv].[SQL_TopProcedureExecStat] ([DB_ID],[OBJECT_ID] ,[ExecutionCount] ,[TotalWorkerTime],[TotalElapsedTime],[AvgWorkerSec] ,[AvgElapsed]NAME_NAME],[AvgElapsed] InsertutCdate, CategoryName, TotalPhysicalreads, Totallogicalreads, Totallogicalwrits, Avgphysicalreads, Avglogicalreads, Avglogicalwrits) Select Top (@Top) [Database_ID], [Object_ID], [Num], [TotalWorkerTime], [Totalelapsedtid], [Avgworkersec], [Avgelapledsec [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. For eksempel:

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).

Konklusion

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. Sådan fjerner du en kolonne i SQL Server ved hjælp af T-SQL

  2. Sådan håndteres et enkelt tilbud i Oracle SQL

  3. Indsæt flere rækker med PDO-forberedte erklæringer

  4. MySQL-forespørgselsstreng indeholder