Förord
Förr eller senare skulle en DB-administratör vilja ha en prestandaindikator för SQL Server-frågor. Som vi alla vet kommer att köra Profiler i 24 timmar att leda till en betydande systembelastning och därför kan det inte anses vara en optimal lösning för databaser som används i 24/7-läget.
Så, hur kan vi upptäcka tillståndet för SQL Server-frågor? Hur kan vi spåra upptäckta frågerelaterade problem utan mänsklig input?
I den här artikeln kommer jag att tillhandahålla en implementering av SQL Server-prestandaindikatorn för frågor, lagrade procedurer och utlösare, såväl som dess användning för spårningskörningen.
Lösning
Först och främst, låt oss ta en titt på den allmänna strategin för implementering av prestandaindikatorn för frågor, lagrade procedurer och triggers:
- Skapa obligatoriska tabeller för informationsinsamling och analys.
- Skapa en vy för informationsinsamling.
- Skapande av lagrade procedurer för informationsinsamling.
- Skapa en vy för informationsutmatning.
Och nu, låt oss överväga implementeringen:
1. Skapande av nödvändiga tabeller för informationsinsamling och analys.
1.1. För frågor:
ANVÄND [DATABASE_NAME]GOSET ANSI_NULLS ONGOSET QUOTED_IDENTIFIER ONGOSET ANSI_PADDING ONGOCREATE TABLE [srv].[SQL_StatementExecStat]( [ID] [bigint] IDENTITET(1,1) INTE NULL] [Datum] NULL, [Datum] [Datum] [Kid] binär](8) NULL, [ExecutionCount] [bigint] NULL, [TotalWorkerTime] [bigint] NULL, [StatementText] [nvarchar](max) NULL, [TotalElapsedTime] [bigint] NULL, CONSTRAINT [PK_SQL_StatementExec KEYStat] [ID] ASC)MED (PAD_INDEX =AV, STATISTICS_NORECOMPUTE =AV, IGNORE_DUP_KEY =AV, ALLOW_ROW_LOCKS =PÅ, ALLOW_PAGE_LOCKS =PÅ) PÅ [PRIMÄR]) PÅ [PRIMÄR] TEXTIMAGE_PÅ [PRIMÄR] TEXTIMAGE_PÅ [PÅ]1.2. För lagrade procedurer:
ANVÄND [DATABASE_NAME]GOSET ANSI_NULLS ONGOSET QUOTED_IDENTIFIER ONGOCREATE TABLE [srv].[SQL_ProcedureExecStat]( [ID] [bigint] IDENTITET(1,1) INTE NULL, [Infoga Date] [InsertDate], [Datetime]_ID] 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ÄRNYCKEL KLUSTERAD ( [ID] ASC)MED (PAD_INDEX =AV, STATISTICS_NORECOMPUTE =AV, IGNORE_DUP_KEY =AV_TILLÅT_TILLÅT, [TILLÅT_TILLÅT_) PRIMARY]GO1.3. För utlösare:
ANVÄND [DATABASE_NAME]GOSET ANSI_NULLS ONGOSET QUOTED_IDENTIFIER ONGOCREATE TABLE [srv].[SQL_TriggerExecStat]( [ID] [bigint] IDENTITET(1,1) INTE NULL, [Infoga datum] [datum]_databas] NULL] NULL, [object_id] [int] NULL, [ExecutionCount] [bigint] NULL, [TotalWorkerTime] [bigint] NULL, [TotalElapsedTime] [bigint] NULL) PÅ [PRIMÄR]GO2. Skapa en vy för informationsinsamling (här kan vi infoga filter för att bli av med den irrelevanta informationen (till exempel frågor och procedurer med replikeringsutlösare etc).
2.1. För frågor:ANVÄND [DATABASE_NAME]GOSET ANSI_NULLS ONGOSET QUOTED_IDENTIFIER ONGOCREATE vy [srv].[vStatementExecInfo] som med info som (SELECT query_stats.query_hash AS QueryHash, SUM(query_stats.total_worker_time)/exekveringstid ASUM(exekvering_tid)SUM(exekveringstid)SUM(exekveringstid) 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_query_querystime, MAX_query_querytalstime, max._query_querytalstime, max. 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 MaxLogicalquery_logical_writes.Avritation_toquery_logstat_, 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.Relapad_querysMIN(query_stats.total_logical_reads). 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_worker_time,Qs_worker_time,Qs_scal_ .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_time_laps. ,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) ANDERS QS.statement_end_offset END - QS.offset)_start_statement 2) + 1) AS statement_text FRÅN 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 que_hour(hoursha) )välj QueryHash, AvgCPU_Time, ExecutionCount, TotalWorkerTime, StatementText, MinWorkerTime, MaxWorkerTime, TotalPhysicalReads, MinPhysicalReads, MaxPhysicalReads, AvgPhysicalReads, TotalLogicalWrites, MinLogicalWritical, MinLogicalWritical, MaxReadLogical, MaxReadLogical, MinLogical, AvgLogicalReads, TotalElapsedTime, MinElapsedTime, MaxElapsedTime, AvgElapsedTime, MinCreationTime, LastExecuteTimefrom infoGOHär används följande systemfrågor:sys.dm_exec_query_stats och sys.dm_exec_sql_text.
2.2. För lagrade procedurer:ANVÄND [DATABASE_NAME]GOSET ANSI_NULLS ONGOSET QUOTED_IDENTIFIER ONGOCREATE vy [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,SUM(proced_stats,sUM() .total_worker_time ) / SUM(procedure_stats.execution_count) AS AvgCPU_Time, SUM(procedurstats.execution_count ) AS ExecutionCount, SUM(procedure_stats.total_worker_time ) AS TotalWorkerTime, MIN(procedur_stats.ProcedureText )_ AS_min_tid,procedur ,min_procedur, MAX_procedur (procedure_stats.max_worker_time ) AS MaxWorkerTime, SUM(procedurstats.total_physical_reads) AS TotalPhysicalReads, MIN(procedurstats.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(procedur_logiska_statistik_totalsmin. procedure_stats.max_logical_writes ) AS MaxLogicalWrites, SUM(procedur_stats.total_logical_writes) / SUM(procedure_stats.execution_count) AS AvgLogicalWrites, SUM(procedure_stats.total_logical_reads ) AS TotalLogicalReads, Minslogical_procedur AS_Read_max_procedur, MINs_procedur_max. SUM(procedurstats.total_logical_reads ) / SUM(procedure_stats.execution_count) AS AvgLogicalReads, SUM(procedurstats.total_elap sed_time ) AS TotalElapsedTime, MIN(procedur_stats.min_elapsed_time ) AS MinElapsedTime, MAX(procedure_stats.max_elapsed_time ) AS MaxElapsedTime, SUM(procedure_stats.total_elapsed_time ) / SUM(procedur_stats.execution_counted_max)ASgEprocessed. 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_total,QS_read physical_to. 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 database_Time, Excuid_Count, object_ TotalWorkerTime, ProcedureText, MinWorkerTime, MaxWorkerTime, TotalPhysicalReads, MinPhysicalReads, MaxPhysicalRead s, AvgPhysicalReads, TotalLogicalWrites, MinLogicalWrites, MaxLogicalWrites, AvgLogicalWrites, TotalLogicalReads, MinLogicalReads, MaxLogicalReads, AvgLogicalReads, TotalElapsedTime, MinElapsedTime, MinElapsedTime, MinElapsElapeTid, MinElapsElapeTid, MinElapsElapeTid, MinElapsExtim.Här används följande systemfrågor:sys.dm_exec_Procedure_stats och sys.dm_exec_sql_text.
2.3. För utlösare:
ANVÄND [DATABASE_NAME]GOSET ANSI_NULLS ONGOSET QUOTED_IDENTIFIER ONGOCREATE vy [srv].[vTriggerExecInfo] som med info som (SELECT procedure_stats.database_id AS database_id, procedure_stats.object_id AS object_id, SUM(procedur_stats,SUM(procedur_stats,sUM(proced_stats,sUM() .total_worker_time ) / SUM(procedure_stats.execution_count) AS AvgCPU_Time, SUM(procedurstats.execution_count ) AS ExecutionCount, SUM(procedure_stats.total_worker_time ) AS TotalWorkerTime, MIN(procedur_stats.ProcedureText )_ AS_min_tid,procedur ,min_procedur, MAX_procedur (procedure_stats.max_worker_time ) AS MaxWorkerTime, SUM(procedurstats.total_physical_reads) AS TotalPhysicalReads, MIN(procedurstats.min_fys 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(procedur_stats_totalsMIN)Procedur_stats_totalsMIN(procedur_stats_totalsMIN) Totalt AS_Writical_Writical_Writical AS_Writical(procedur_stats_total)Min_procedur_Writical_Writical(procedur_stats_total) procedure_stats.max_logical_writes ) AS MaxLogicalWrites, SUM(procedur_stats.total_logical_writes) / SUM(procedure_stats.execution_count) AS AvgLogicalWrites, SUM(procedure_stats.total_logical_reads ) AS TotalLogicalReads, Minslogical_procedur AS_Read_max_procedur, MINs_procedur_max. SUM(procedure_stats.total_logical_reads ) / SUM(procedurstats.exekveringsantal) AS AvgLogicalReads, SUM(procedure_stats.total_elapse d_time ) AS TotalElapsedTime, MIN(procedur_stats.min_elapsed_time ) AS MinElapsedTime, MAX(procedure_stats.max_elapsed_time ) AS MaxElapsedTime, SUM(procedure_stats.total_elapsed_time ) / SUM(procedur_stats.MaxElapsedTime)ASgElapsedTime(procedur_stats.MaxElapsedTime)ASgElapsedTime(MaxElapsedTime)AS 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_total,QS_read physical_to. 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(hour,-3,getdate())GROUP BY database_id,object_id)välj databas_id, avg_CPU-typ, Total_Cution, Total_CPU , ProcedureText, MinWorkerTime, MaxWorkerTime, TotalPhysicalReads, MinPhysicalReads, MaxPhysicalReads, AvgPhysicalReads, TotalLogicalWrites, MinLogicalWrites, MaxLogicalWrites, AvgLogicalWrites, TotalLogicalReads, MinLogicalReads, MaxLogicalReads, AvgLogicalReads, TotalElapsedTime, MinElapsedTime, MaxElapsedTime, MaxElapsedTime, MaxElapsedTime, AvgElapsedTime, AvgElapsedTime, InfoHär används följande systemfrågor:sys.dm_exec_trigger_stats och sys.dm_exec_sql_text.
3. Skapande av lagrade rutiner för informationsinsamling.
3.1. För frågor:
ANVÄND [DATABASE_NAME]GOSET ANSI_NULLS ONGOSET QUOTED_IDENTIFIER ONGOCREATE PROCEDURE [srv].[InsertForSQL_StatementExecStat] @koef decimal(12,2)=0.0 –insamlingskoefficient --plockas på ett experimentellt sätt för mer exakta insamling fall kan vi sätta 0,0, --om frekvensen av insamlingen som körs inte överstiger 5 minuter. --Beräkningens noggrannhet beror på insamlingsfrekvensen och insamlingskoefficienten. --Ju mer frekvent insamling körs, desto mindre inflytande har insamlingskoefficienten. Förklara @avgcpu_time bigint,@maxavgcpu_time bigint,@avgtotalworkertime bigint,@mextotalworkertime bigint,@avgavelapsedtime bigint,@maxavgelapsedimeTime Bigint,@avgtotalapsedTime BigIn AvgTotalWorkerTime =AVG(TotalWorkerTime), @MaxTotalWorkerTime =max(TotalWorkerTime), @AvgAvgElapsedTime =AVG(AvgElapsedTime), @MaxAvgElapsedTime =max(AvgElapsedTime), @AvgTotalElapsedTime.; infoga i srv.SQL_StatementExecStat ( [InsertDate] ,[QueryHash], [ExecutionCount], [TotalWorkerTime], [StatementText],[TotalElapsedTime]) välj getdate() ,[QueryHash] ,[ExecutionCount]W,[ExecutionCount]W,[Execution Count] ,[TotalElapsedTime] från srv.vStatementExecInfo where(AvgCPU_Time> @AvgCPU_Time + @koef * (@MaxAvgCPU_Time - @AvgCPU_Time)) or (TotalWorkerTime> @AvgTotalWorkerTime + @AvgTotalWorkerTime + @AvgTotalWorkerTime + @AvgTotalWorkerTime + @kAvgtox) + @koef * (@MaxAvgElapsedTime - @AvgAvgElapsedTime)) eller (TotalElapsedTime> @AvgTotalElapsedTime + @koef * (@MaxTotalElapsedTime - @AvgTotalElapsedTime));ENDGO3.2. För lagrade procedurer:
ANVÄND [DATABASE_NAME]GOSET ANSI_NULLS ONGOSET QUOTED_IDENTIFIER ONGOCREATE PROCEDURE [srv].[InsertForProcedureExecStat] @koef decimal(12,2)=0.0 --insamlingskoefficient --plockas på ett experimentellt sätt för mer exakt insamling, --på ett mer exakt sätt i de flesta fall kan vi sätta 0,0, -- om frekvensen för insamlingen som körs inte överstiger 5 minuter. --Beräkningens noggrannhet beror på insamlingsfrekvensen och insamlingskoefficienten. --Ju mer frekvent insamling körs, desto mindre inflytande har insamlingskoefficienten. deklarera @AvgCPU_Time bigint ,@MaxAvgCPU_Time bigint ,@AvgTotalWorkerTime bigint ,@MaxTotalWorkerTime bigint ,@AvgAvgElapsedTime bigint ,@MaxAvgElapsedTime bigint ,@AvgTotalTid bigint,Elapsed@AvgTotalElapsed@AvgTotalElaps; välj @AvgCPU_Time =AVG(AvgCPU_Time), @MaxAvgCPU_Time =max(AvgCPU_Time), @AvgTotalWorkerTime =AVG(TotalWorkerTime), @MaxTotalWorkerTime =max(TotalWorkerTime), @AvgAvgElapsedTime =ElapsedAvgMaxElapsedAvgMaxA AvgTotalElapsedTime =AVG(TotalElapsedTime), @MaxTotalElapsedTime =max(TotalElapsedTime) från srv.vProcedureExecInfo; infoga i srv.SQL_ProcedureExecStat ( [InsertDate] ,database_id ,object_id ,[ExecutionCount] ,[TotalWorkerTime],[TotalElapsedTime] ,[TotalPhysicalReads] ,[TotalLogicalReads] ,[TotalLogicalReads] ,[ExecutionCount] ,[ExecutionCount] ,[ExecutionTotal ,[TotalElapsedTime] ,[TotalLogicalReads] ,[TotalLogicalReads] ,[ExecutionTotal] ,[Execution Count] ,[Ext. ,[TotalWorkerTime] ,[TotalElapsedTime] ,[TotalPhysicalReads] ,[TotalLogicalReads] ,[TotalLogicalWrites] från srv.vProcedureExecInfo där(AvgCPU_Time> @MavgCPU_Time @MavgCPU_Time @xAOgTid @xAOgTid @xAOftal *(CvKrTid @xAOftal) + @xAOftal * (W_A) koef * (@MaxTotalWorkerTime - @AvgTotalWorkerTime)) eller (AvgElapsedTime> @AvgAvgElapsedTime + @koef * (@MaxAvgElapsedTime - @AvgAvgElapsedTime)) eller (TotalElapsedTime @MaxElapsedTime> @ElapsedgTotal(+ElapsedgTotal) apsedTime - @AvgTotalElapsedTime));ENDGO3.3. För utlösare:
ANVÄND [DATABASE_NAME]GOSET ANSI_NULLS ONGOSET QUOTED_IDENTIFIER ONGOCREATE PROCEDURE [srv].[InsertForTriggerExecStat] @koef decimal(12,2)=0.0 --insamlingskoefficient --plockas på ett experimentellt sätt för mer exakt insamling, --på ett mer exakt sätt i de flesta fall kan vi sätta 0.0, --om frekvensen för insamlingen som körs inte överstiger 5 minuter. --Beräkningens noggrannhet beror på insamlingsfrekvensen och insamlingskoefficienten. --Ju mer frekvent insamling körs, desto mindre inflytande har insamlingskoefficienten. Förklara @avgcpu_time bigint,@maxavgcpu_time bigint,@avgtotalworkertime bigint,@mextotalworkertime bigint,@avgavelapsedtime bigint,@maxavgelapsedimeTime Bigint,@avgtotalapsedTime BigIn AvgTotalWorkerTime =AVG(TotalWorkerTime), @MaxTotalWorkerTime =max(TotalWorkerTime), @AvgAvgElapsedTime =AVG(AvgElapsedTime), @MaxAvgElapsedTime =max(AvgElapsedTime =max(AvgElapsedTime); infoga i srv.SQL_TriggerExecStat ( [InsertDate] ,database_id ,object_id ,[ExecutionCount] ,[TotalWorkerTime],[TotalElapsedTime]) välj getdate() ,database_id ,object_id ,[ExecutionCount]orTokervTid from[ExecutionTotalWInfo] ,[ecvr where(AvgCPU_Time> @AvgCPU_Time + @koef * (@MaxAvgCPU_Time - @AvgCPU_Time)) or (TotalWorkerTime> @AvgTotalWorkerTime + @koef * (@MaxTotalWorkerTime - @AvgTotalWorkerTime @AvgTotalWorkerTime @AvgTotalWorkerTime @AvgTotalWorkerTime))) eller (Avg>Evg. @AvgAvgElapsedTime)) eller (TotalElapsedTime> @AvgTotalElapsedTime + @koef * (@MaxTotalElapsedTime - @AvgTotalElapsedTime));ENDGO4. Skapa en vy för informationsutmatningen.
4.1. För frågor:
ANVÄND [DATABASE_NAME]GOSET ANSI_NULLS ONGOSET QUOTED_IDENTIFIER ONGOCREATE VISNING [srv].[vStatementExecTotalInfo]välj ExecutionCount as Num ,TotalWorkerTime as TotalWorkerTime ,TotalElapsedTime as TotalElapsedTime as TotalElapsedTime as TotalElapsedTime as TotalElapsedTime as TotalElapsedTime as TotalElapsedTime as TotalElaps00C,WP convert(decimal(8,2),AvgElapsedTime/1000000.) as AvgElapsedSec ,... ,QueryHash ,StatementText from [SRV].[srv].[vStatementExecInfo];GO4.2. För lagrade procedurer:
ANVÄND [DATABASE_NAME]GOSET ANSI_NULLS ONGOSET QUOTED_IDENTIFIER ONGOCREATE VISNING [srv].[vProcedureExecTotalInfo]som välj ExecutionCount as Num ,TotalWorkerTime som TotalWorkerTime ,TotalElapsedTime as TotalElapsedTime as TotalElapsedTime as TotalElapsedTime as TotalElapsedTime as TotalElapsedTime as TotalElapsedTime as TotalElapsedTime as TotalElapsedTime as TotalElapsedTime as TotalEc00g0g ,convert(decimal(8,2),AvgElapsedTime/1000000.) as AvgElapsedSec ,... ,databas_id ,object_id ,db_name(databas_id) as DB_Name ,OBJECT_SCHEMA_NAME(object_id, database_id) som Schema_Name_Name_ från [SRV].[srv].[vProcedureExecInfo];GO4.3. Vyer för utlösare skapas på liknande sätt (om det behövs). När det gäller mig behöver jag inte spårningstriggers, eftersom om det finns några problem med triggers, kommer exekvering av lagrade procedurer och frågor att visa dem.
Följande två parametrar har avgörande betydelse för de implementerade vyerna:
- AvgWorkerSec — sökexekveringstiden i sekunder.
- AvgElapsedSec — väntetid eller väntetid+AvgWorkerSec.
När det gäller resultaten av synpunkter är följande jämställdhet viktig:
AvgWorkerSec=AvgElapsedSec
- AvgWorkerSec>AvgElapsedSec – här belastar något processorn kraftigt i ögonblicket av exekveringen av en fråga (som det visade sig kördes genomsökningen av antivirusprogramvaran; det kan också vara felet i planen som parallellkopplas).
- AvgWorkerSec
Om AvgWorkerSec=AvgElapsedSec följs, är den långa körningstiden relaterad till själva frågan och dess körningstid.
Vad är ett kriterium för den långa frågekörningen?
Det finns inget absolut svar på denna fråga. Det beror på vad en fråga gör, var och hur den används etc.
Jag har följande utvärdering för ad hoc-frågor och lagrade procedurer:
- Upp till 0,5 – bra för lagrade procedurer, inga problem (inga körningsväntningar).
- Upp till 0.1 – bra för frågor, inga problem (inga körningsväntningar).
- 0,5 – 1,0 – dåligt för lagrade procedurer, det finns problem (det finns inga körningsväntningar som är synliga för en användare, men de finns fortfarande och kräver lösning).
- 0.1 — 0.5 — dåligt för frågor, det finns problem (det finns inga körningsväntningar som är synliga för en användare, men de finns fortfarande och kräver att lösas).
- Mer än 1.0 – dåligt för lagrade procedurer, det finns problem (det finns en stor chans att det finns väntan som är synliga för användarna, problemet kräver omedelbar lösning).
- Mer än 0,5 – dåligt för frågor, det finns problem (det finns en stor chans att det finns väntetider som är synliga för användarna, problemet kräver omedelbar lösning).
När det gäller de icke-ad hoc-frågor (datauppladdning, dataladdning) väljs ovanstående utvärdering på individuell basis. Vanligtvis överskrider det massivt utvärderingar för ad hoc-frågor och lagrade procedurer.
Om all programvara fungerar genom de lagrade procedurerna kan du spåra endast lagrade procedurer, eftersom arbetet med frågor alltid påverkar arbetet med lagrade procedurer. Det är därför, låt oss nöja oss med analys av exekveringen av lagrade procedurer.
Låt oss skapa ett system för att samla in information om de mest tunga lagrade procedurerna för efterföljande analys och körning av autospårning, enligt följande algoritm:
1. Skapa en tabell för att lagra information:
ANVÄND [DATABASE_NAME]GOSET ANSI_NULLS ONGOSET QUOTED_IDENTIFIER ONGOCREATE TABLE [srv].[SQL_TopProcedureExecStat]( [Row_GUID] [uniqueidentifier] INTE NULL, [SERVER] [nvarchar] [nvarchar](255) NOT NULL, [int] , [OBJECT_ID] [int] NOT NULL, [ExecutionCount] [bigint] NOT NULL, [TotalWorkerTime] [bigint] NULL, [TotalElapsedTime] [bigint] NULL, [Func] [decimal](8, 2) NULL, [AvgWorkerS ] [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, [AvgReadAhysical] [AvgReadAhysical] ] [bigint] NULL, [AvgLogicalWrites] [bigint] NULL, [CategoryName] [nvarchar](255) NULL, CONSTRAINT [PK_ Ex SQL_TopProcedureExecStat] PRIMÄR NYCKEL KLUSTERAD ( [Row_GUID] ASC) MED (PAD_INDEX =AV, STATISTICS_NORECOMPUTE =AV, IGNORE_DUP_KEY =AV, ALLOW_ROW_LOCKS =PÅ, ALLOW_INDEX =AV, STATISTICS_NORECOMPUTE =AV, IGNORE_DUP_KEY =AV, ALLOW_ROW_LOCKS =PÅ, ALLOW_INDEX =FRÅN [QLop. ] LÄGG TILL BEGRÄNSNING [DF_SQL_TopProcedureExecStat_Row_GUID] DEFAULT (newid()) FÖR [Row_GUID]MÅLTABELL [srv].[SQL_TopProcedureExecStat] LÄGG TILL BEGRÄNSNING [DF_SQL_TopProcedureExecStat(@SERVER]DEVERT_SQL_TopProcedureExecStat_SERVER]DEVERT_Procedure(@SERVER]DEV.ProcedL) CONSTRAINT [DF_SQL_TopProcedureExecStat_InsertUTCDate] DEFAULT (getutcdate()) FÖR [InsertUTCDate]GO2. Skapande av en lagrad procedur för att samla in information:
ANVÄND [DATABASE_NAME]GOSET ANSI_NULLS ONGOSET QUOTED_IDENTIFIER ONGOCREATE PROCEDURE [srv].[InsertTopProcedureExecStat] @top tinyint=24 – antal dagar för att lagra poster ,@CategoryName nvarchar(AecWAS) kategori –BEGINSET a COUNT förSÄTTA en kategori (255) PÅ; INSERT INTO [srv].[SQL_TopProcedureExecStat] ([DB_ID],[OBJECT_ID] ,[ExecutionCount] ,[TotalWorkerTime],[TotalElapsedTime],[AvgWorkerSec] ,[AvgElapsed]NAME_NAME] ,[AvgElapsed]NAME_NAME],[NAMN] Insertutcdate, kategoryName, TotalPhysicalReads, TotallogicalReads, TotallogicalWrites, AvgPhysicalReads, AvGogicalReads, AvGogicalWrites) Select Top (@top] [database_id], [object_id], [num], [TotalWerDime], [TotalelapsedTeTime], [AVOWWORKERSEC], [avggaps], [numapsedsedsedsedsedsedsedsedsedsedsedsedsedsediedsedsEM], ^ [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]; endThe auto-trace stored procedure is implemented on an individual basis. Till exempel:
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).
Slutsats
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)