Voorwoord
Vroeg of laat wil een databasebeheerder een prestatie-indicator hebben voor SQL Server-query's. Zoals we allemaal weten, zal het gedurende 24 uur draaien van Profiler leiden tot een aanzienlijke systeembelasting en daarom kan het niet worden beschouwd als een optimale oplossing voor databases die in de 24/7-modus worden gebruikt.
Dus, hoe kunnen we de status van SQL Server-query's detecteren? Hoe kunnen we traceren voor gedetecteerde query-gerelateerde problemen zonder menselijke input?
In dit artikel zal ik een implementatie geven van de SQL Server-prestatie-indicator voor query's, opgeslagen procedures en triggers, evenals het gebruik ervan voor de tracering.
Oplossing
Laten we eerst eens kijken naar de algemene benadering van de implementatie van de prestatie-indicator voor queries, opgeslagen procedures en triggers:
- Aanmaken van vereiste tabellen voor het verzamelen en analyseren van informatie.
- Een weergave maken voor het verzamelen van informatie.
- Creëren van opgeslagen procedures voor het verzamelen van informatie.
- Creëren van een weergave voor informatie-output.
En laten we nu eens kijken naar de implementatie:
1. Creatie van vereiste tabellen voor het verzamelen en analyseren van informatie.
1.1. Voor vragen:
USE [DATABASE_NAME]GOSET ANSI_NULLS ONGOSET QUOTED_IDENTIFIER ONGOSET ANSI_PADDING ONGOCREATE TABLE [srv].[SQL_StatementExecStat]( [ID] [bigint] IDENTITY(1,1) NOT NULL, [InsertDate] [datetime]Nash binair](8) NULL, [ExecutionCount] [bigint] NULL, [TotalWorkerTime] [bigint] NULL, [StatementText] [nvarchar](max) NULL, [TotalElapsedTime] [bigint] NULL, CONSTRAINT [PK_SQL_StatementExecStat] PRIMAIRE (SLEUTEL [ID] ASC)MET (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 ANGO_PADDING1.2. Voor opgeslagen procedures:
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] PRIMARY KEY CLUSTERED ( [ID] ASC)MET (PAD_INDEX =OFF, STATISTICS_NORECOMPUTE =OFF, IGNORE_DUP_KEY =OFF, ALLOW_ROW_LOCKS =ON) PRIMARY]GO1.3. Voor triggers:
USE [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]GO2. Creëren van een weergave voor het verzamelen van informatie (hier kunnen we filters invoegen om de irrelevante informatie te verwijderen (bijvoorbeeld query's en procedures met replicatietriggers, enz.).
2.1. Voor query's:USE [DATABASE_NAME]GOSET ANSI_NULLS ONGOSET QUOTED_IDENTIFIER ONGOCREATE bekijk [srv].[vStatementExecInfo] als met info als (SELECT query_stats.query_hash AS QueryHash, SUM(query_stats.total_worker_timeque ) / SUM(query_stats.total_worker_timeque ) / 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 MinWorkerstats.total_worker_time, MAX(query_stats_time)max_worker 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 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(querylogical_statsques.total_ 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.execution_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_Sworker_time .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_time_elapsed_time ,Qd_time ,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.statement_start_offset)/ 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(hour,-3,getdateBY())stat )selecteer QueryHash, AvgCPU_Time, ExecutionCount, TotalWorkerTime, StatementText, MinWorkerTime, MaxWorkerTime, TotalPhysicalReads, MinPhysicalReads, MaxPhysicalReads, AvgPhysicalReads, TotalLogicalWrites, MinLogicalWrites, MaxLogicalWrites,Reads, MaxLogicalWrites, MaxLogicalWrites, AvgLogicalReads, TotalElapsedTime, MinElapsedTime, MaxElapsedTime, AvgElapsedTime, MinCreationTime, LastExecuteTimevan infoGOHier worden de volgende systeemquery's gebruikt:sys.dm_exec_query_stats en sys.dm_exec_sql_text.
2.2. Voor opgeslagen procedures:USE [DATABASE_NAME]GOSET ANSI_NULLS ONGOSET QUOTED_IDENTIFIER ONGOCREATE weergave [srv].[vProcedureExecInfo] as met info as (SELECT procedure_stats.database_id AS database_id, procedure_stats.object_id AS object_id, MIN(procedure_stats, S.type) ASstats .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(procedure_stats.ProcedureMAX, ) AS Proceduret_stats.ProcedureMAX, ) AS Proceduret (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 AvgPhysical_stats, SUM(procedurelogische_stats.total) 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, MIN(procedure_stats.min_minlogische_stats.min_logische_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(procedure_stats.execution_count) ASchedg_g 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.Qreads_phys totale_logische_schrijven, 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 exe ,QS.ca.ca 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_id, object_PU_Time, Avgion_PU_Time TotalWorkerTime, ProcedureText, MinWorkerTime, MaxWorkerTime, TotalPhysicalReads, MinPhysicalReads, MaxPhysicalRead s, AvgPhysicalReads, TotalLogicalWrites, MinLogicalWrites, MaxLogicalWrites, AvgLogicalWrites, TotalLogicalReads, MinLogicalReads, MaxLogicalReads, AvgLogicalReads, TotalElapsedTime, MinElapsedTime, MaxElapsedTime, AvgElapsedTime, Ex MinCachedTime, infoGOHier worden de volgende systeemquery's gebruikt:sys.dm_exec_Procedure_stats en sys.dm_exec_sql_text.
2.3. Voor triggers:
GEBRUIK [DATABASE_NAME]GOSET ANSI_NULLS ONGOSET QUOTED_IDENTIFIER ONGOCREATE bekijk [srv].[vTriggerExecInfo] als met info als (SELECT procedure_stats.database_id AS database_id, procedure_stats.object_id AS object_id, MIN(procedure_stats, S.type) ASstats .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(procedure_stats.ProcedureMAX, ) AS Proceduret_stats.ProcedureMAX, ) AS Proceduret (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 AvgPhysicalWrits, SUM(MAXLogical_stats.total_) 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, MIN(procedure_stats.min_minlogische_stats.min_logische_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.execution_count) AS Avgched_stats.execution_count) 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.Qreads_phys totale_logische_schrijven, 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_time ,QSQcacuted_time ,QSQcacuted_time 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_id, object_id_Total, ExecutionTimeCount , ProcedureText, MinWorkerTime, MaxWorkerTime, TotalPhysicalReads, MinPhysicalReads, MaxPhysicalReads, AvgPhysicalReads, TotalLogicalWrites, MinLogicalWrites, MaxLogicalWrites, AvgLogicalWrites, TotalLogicalReads, MinLogicalReads, MaxLogicalReads, AvgLogicalReads, TotalelapsedTime, MinElapsedTime, MaxElapsedTime, AvgElapsedTime, MinCachedTimefrom infoGOExecute>Timefrom infoGOExecute>Hier worden de volgende systeemquery's gebruikt:sys.dm_exec_trigger_stats en sys.dm_exec_sql_text.
3. Creëren van opgeslagen procedures voor het verzamelen van informatie.
3.1. Voor vragen:
USE [DATABASE_NAME]GOSET ANSI_NULLS ONGOSET QUOTED_IDENTIFIER ONGOCREATE PROCEDURE [srv].[InsertForSQL_StatementExecStat] @koef decimal(12,2)=0.0 –collectioncoëfficiënt --wordt op een experimentele manier gekozen voor een nauwkeurigere verzameling, --in de meeste gevallen kunnen we 0,0, --als de frequentie van de verzameling niet langer is dan 5 minuten. --Nauwkeurigheid van de berekening hangt af van de ophaalfrequentie en de ophaalcoëfficiënt. --Hoe vaker de incasso wordt uitgevoerd, hoe minder invloed de collectiecoëfficiënt heeft. ASBEGIN STEL NOCOUNT IN; declareren @AvgCPU_Time bigint ,@MaxAvgCPU_Time bigint ,@AvgTotalWorkerTime bigint ,@MaxTotalWorkerTime bigint ,@AvgAvgElapsedTime bigint,@MaxAvgElapsedTime bigint,@AvgTotalElapsedTime bigint),(MaxTotalElapsedTime bigintAvCPU_Time bigintAvCPU_Time bigintAvCPU_Time bigintAv AvgTotalWorkerTime =AVG(TotalWorkerTime), @MaxTotalWorkerTime =max(TotalWorkerTime), @AvgAvgElapsedTime =AVG(AvgElapsedTime), @MaxAvgElapsedTime =max(AvgElapsedTime), @AvgTotalElapsedTime =AVG(TotalElapsedExlapsedTime), @MaxAvgElapsedTime; invoegen in srv.SQL_StatementExecStat ( [InsertDate] ,[QueryHash] ,[ExecutionCount] ,[TotalWorkerTime] ,[StatementText] ,[TotalElapsedTime]) selecteer getdate() ,[QueryHash] ,[ExecutionCounterTijd] ,[TotalElapsedTime] van srv.vStatementExecInfo waar(AvgCPU_Time> @AvgCPU_Time + @koef * (@MaxAvgCPU_Time - @AvgCPU_Time)) of (TotalWorkerTime> @AvgTotalWorkerTime + @koef *(MaxTotalWorkerTime - @AvgCPU_Time) + @koef * (@MaxAvgElapsedTime - @AvgAvgElapsedTime)) of (TotalElapsedTime> @AvgTotalElapsedTime + @koef * (@MaxTotalElapsedTime - @AvgTotalElapsedTime));ENDGO3.2. Voor opgeslagen procedures:
USE [DATABASE_NAME]GOSET ANSI_NULLS ONGOSET QUOTED_IDENTIFIER ONGOCREATE PROCEDURE [srv].[InsertForProcedureExecStat] @koef decimal(12,2)=0.0 --collectioncoëfficiënt --wordt op een experimentele manier gekozen voor een nauwkeurigere verzameling, --in in de meeste gevallen kunnen we 0.0,- zetten als de frequentie van de collectie niet langer is dan 5 minuten. --Nauwkeurigheid van de berekening hangt af van de ophaalfrequentie en de ophaalcoëfficiënt. --Hoe vaker de incasso wordt uitgevoerd, hoe minder invloed de collectiecoëfficiënt heeft. ASBEGIN STEL NOCOUNT IN; declareren @AvgCPU_Time bigint ,@MaxAvgCPU_Time bigint ,@AvgTotalWorkerTime bigint ,@MaxTotalWorkerTime bigint ,@AvgAvgElapsedTime bigint ,@MaxAvgElapsedTime bigint ,@AvgTotalElapsedTime bigint ,@MaxTotal; selecteer @AvgCPU_Time =AVG(AvgCPU_Time), @MaxAvgCPU_Time =max(AvgCPU_Time), @AvgTotalWorkerTime =AVG(TotalWorkerTime), @MaxTotalWorkerTime =max(TotalWorkerTime), @AvgAvgElapsedTime =AVG(AvgMaxElapsedTime), @AvgAvgElapsedTime =(AvgMaxElapsedTime), @Tijd AvgTotalElapsedTime =AVG(TotalElapsedTime), @MaxTotalElapsedTime =max(TotalElapsedTime) van srv.vProcedureExecInfo; invoegen in srv.SQL_ProcedureExecStat ( [InsertDate], database_id, object_id, [ExecutionCount], [TotalWorkerTime], [TotalElapsedTime], [TotalPhysicalReads], [TotalLogicalReads], ,[TotalWorkerTime] ,[TotalElapsedTime] ,[TotalPhysicalReads] ,[TotalLogicalReads] ,[TotalLogicalWrites] van srv.vProcedureExecInfo waar(AvgCPU_Time> @AvgCPU_Time + @koef * (@MaxAvgCPUTime) koef * (@MaxTotalWorkerTime - @AvgTotalWorkerTime)) of (AvgElapsedTime> @AvgAvgElapsedTime + @koef * (@MaxAvgElapsedTime - @AvgAvgElapsedTime)) of (TotalElapsedTime> @AvgTotalElapsedTime + @koef * (@Max apsedTime - @AvgTotalElapsedTime));ENDGO3.3. Voor triggers:
USE [DATABASE_NAME]GOSET ANSI_NULLS ONGOSET QUOTED_IDENTIFIER ONGOCREATE PROCEDURE [srv].[InsertForTriggerExecStat] @koef decimal(12,2)=0.0 --collectioncoëfficiënt --wordt op een experimentele manier gekozen voor een nauwkeurigere verzameling, --in in de meeste gevallen kunnen we 0,0, --als de frequentie van de verzameling niet langer is dan 5 minuten. --Nauwkeurigheid van de berekening hangt af van de ophaalfrequentie en de ophaalcoëfficiënt. --Hoe vaker de incasso wordt uitgevoerd, hoe minder invloed de collectiecoëfficiënt heeft. ASBEGIN STEL NOCOUNT IN; declareren @AvgCPU_Time bigint ,@MaxAvgCPU_Time bigint ,@AvgTotalWorkerTime bigint ,@MaxTotalWorkerTime bigint ,@AvgAvgElapsedTime bigint,@MaxAvgElapsedTime bigint,@AvgTotalElapsedTime bigint),(MaxTotalElapsedTime bigintAvCPU_Time bigintAvCPU_Time bigintAvCPU_Time bigintAv AvgTotalWorkerTime =AVG(TotalWorkerTime), @MaxTotalWorkerTime =max(TotalWorkerTime), @AvgAvgElapsedTime =AVG(AvgElapsedTime), @MaxAvgElapsedTime =max(AvgElapsedTime), @AvgTotalElapsedTime =AVG(TotalElapsedExlapsedTime), @MaxAvgElapsedTime =max(AvgElapsedTime), @AvgTotalElapsedTime =AVG(TotalElapsedExlapsedTime), @MaxAvgElapsedTime; invoegen in srv.SQL_TriggerExecStat ( [InsertDate] ,database_id ,object_id ,[ExecutionCount] ,[TotalWorkerTime] ,[TotalElapsedTime]) selecteer getdate() ,database_id,object_id,[ExecutionCount],[TotalWorkerTime] waar(AvgCPU_Time> @AvgCPU_Time + @koef * (@MaxAvgCPU_Time - @AvgCPU_Time)) of (TotalWorkerTime> @AvgTotalWorkerTime + @koef * (@MaxTotalWorkerTime - @AvgTotalWorkerTime)) of (AvgElapsedAvg> @AvAv @AvgAvgElapsedTime)) of (TotalElapsedTime> @AvgTotalElapsedTime + @koef * (@MaxTotalElapsedTime - @AvgTotalElapsedTime));ENDGO4. Creëren van een weergave voor de informatie-output.
4.1. Voor vragen:
GEBRUIK [DATABASE_NAME]GOSET ANSI_NULLS ONGOSET QUOTED_IDENTIFIER ONGOCREATE VIEW [srv].[vStatementExecTotalInfo]asselect ExecutionCount as Num ,TotalWorkerTime als TotalWorkerTime ,TotalElapsedTime als TotalElapsedTimecimal,(convert_2) convert(decimal(8,2),AvgElapsedTime/1000000.) as AvgElapsedSec ,... ,QueryHash ,StatementText van [SRV].[srv].[vStatementExecInfo];GO4.2. Voor opgeslagen procedures:
GEBRUIK [DATABASE_NAME]GOSET ANSI_NULLS ONGOSET QUOTED_IDENTIFIER ONGOCREATE VIEW [srv].[vProcedureExecTotalInfo]as selecteer ExecutionCount als Num ,TotalWorkerTime als TotalWorkerTime ,TotalElapsedTime als TotalElapsedTime/1000000000 ,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) as Schema_Name ,object_nameid(object_id), database van [SRV].[srv].[vProcedureExecInfo];GO4.3. Weergaven voor triggers worden op een vergelijkbare manier gemaakt (indien nodig). Wat mij betreft, ik heb geen traceringstriggers nodig, want als er problemen zijn met triggers, zal de uitvoering van opgeslagen procedures en query's deze tonen.
De volgende twee parameters zijn van vitaal belang voor de geïmplementeerde weergaven:
- AvgWorkerSec — uitvoeringstijd van query in seconden.
- AvgElapsedSec — wachttijd of wachttijd+AvgWorkerSec.
Wat betreft de resultaten van weergaven is de volgende gelijkheid belangrijk:
AvgWorkerSec=AvgElapsedSec
- AvgWorkerSec>AvgElapsedSec – hier wordt de processor zwaar belast op het moment dat de query werd uitgevoerd (het bleek dat de antivirussoftwarescan werd uitgevoerd; het kan ook de fout zijn van het parallelle plan).
- AvgWorkerSec
Als AvgWorkerSec=AvgElapsedSec wordt aangehouden, is de lange uitvoeringstijd gerelateerd aan de query zelf en de uitvoeringstijd ervan.
Wat is een criterium voor de uitvoering van lange zoekopdrachten?
Er is geen absoluut antwoord op deze vraag. Het hangt af van wat een zoekopdracht doet, waar en hoe deze wordt gebruikt, enz.
Ik heb de volgende evaluatie voor de ad-hocvragen en opgeslagen procedures:
- Tot 0,5 – goed voor opgeslagen procedures, geen problemen (geen wachttijden bij uitvoering).
- Tot 0,1 – goed voor zoekopdrachten, geen problemen (geen wachttijden bij uitvoering).
- 0,5 — 1,0 – slecht voor opgeslagen procedures, er zijn problemen (er zijn geen uitvoeringswachten die zichtbaar zijn voor een gebruiker, maar ze bestaan nog steeds en vereisen een oplossing).
- 0,1 — 0,5 — slecht voor zoekopdrachten, er zijn problemen (er zijn geen uitvoeringswachten die zichtbaar zijn voor een gebruiker, maar ze bestaan nog steeds en moeten worden opgelost).
- Meer dan 1.0 – slecht voor opgeslagen procedures, er zijn problemen (de kans is groot dat er wachttijden zijn die zichtbaar zijn voor gebruikers, het probleem vereist onmiddellijke oplossing).
- Meer dan 0,5 – slecht voor vragen, er zijn problemen (er is een grote kans dat er wachttijden zijn die zichtbaar zijn voor gebruikers, het probleem vereist onmiddellijke oplossing).
Wat betreft de niet-ad-hocvragen (upload van gegevens, laden van gegevens), wordt de bovenstaande evaluatie op individuele basis gekozen. Meestal overtreft het de evaluaties voor ad-hocquery's en opgeslagen procedures enorm.
Als alle software via de opgeslagen procedures werkt, kunt u alleen opgeslagen procedures traceren, omdat het werk van query's altijd van invloed is op het werk van opgeslagen procedures. Laten we daarom een analyse maken van de uitvoering van opgeslagen procedures.
Laten we een systeem maken voor het verzamelen van informatie over de zwaarste opgeslagen procedures voor latere analyse en uitvoering van autotrace, volgens het volgende algoritme:
1. Creatie van een tabel voor het opslaan van informatie:
USE [DATABASE_NAME]GOSET ANSI_NULLS ONGOSET QUOTED_IDENTIFIER ONGOCREATE TABLE [srv].[SQL_TopProcedureExecStat]( [Row_GUID] [uniqueidentifier] NOT NULL, [SERVER] [nvarchar](255) NOT NULL [int] NOT NULL, [DB_ID] , [OBJECT_ID] [int] NOT NULL, [ExecutionCount] [bigint] NOT NULL, [TotalWorkerTime] [bigint] NULL, [TotalElapsedTime] [bigint] NULL, [Func] [decimaal] (8, 2) NULL, [AvgWorkersSec ] [decimaal](8, 2) NULL, [AvgElapsedSec] [decimaal](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 ] [bigint] NULL, [AvgLogicalWrites] [bigint] NULL, [Categorienaam] [nvarchar](255) NULL, CONSTRAINT [PK_ SQL_TopProcedureExecStat] PRIMARY KEY CLUSTERED ([Row_GUID] ASC)MET (PAD_INDEX =OFF, STATISTICS_NORECOMPUTE =OFF, IGNORE_DUP_KEY =OFF, ALLOW_ROW_LOCKS =ON, ALLOW_PAGE_LOCKS =ON) ON [PRIMARY]GOALTERS [PRIMARY]GOALTER ] CONSTRAINT TOEVOEGEN [DF_SQL_TopProcedureExecStat_Row_GUID] STANDAARD (newid()) VOOR [Row_GUID]GOALTER TABLE [srv].[SQL_TopProcedureExecStat] CONSTRAINT TOEVOEGEN [DF_SQL_TopProcedureExecStat_SERVER]) DEFATER[ CONSTRAINT [DF_SQL_TopProcedureExecStat_InsertUTCDate] STANDAARD (getutcdate()) VOOR [InsertUTCDate]GO2. Creëren van een opgeslagen procedure voor het verzamelen van informatie:
GEBRUIK [DATABASE_NAME]GOSET ANSI_NULLS ONGOSET QUOTED_IDENTIFIER ONGOCREATE PROCEDURE [srv].[InsertTopProcedureExecStat] @top tinyint=24 – aantal dagen om records op te slaan ,@Categ nam nvarchar(255)='AvgINCategorie voor selectionASec' – NO AAN; INSERT INTO [srv].[SQL_TopProcedureExecStat] ([DB_ID] ,[OBJECT_ID] ,[ExecutionCount] ,[TotalWorkerTime] ,[TotalElapsedTime] ,[AvgWorkerSec] ,[AvgElapsedSec] ,[NAMEDB_NAME_OB] InsertUTCDate ,CategName ,TotalPhysicalReads ,TotalLogicalReads ,TotalLogicalWrites ,AvgPhysicalReads ,AvgLogicalReads ,AvgLogicalWrites) selecteer top(@top) [database_id] ,[object_id] ,[Numer] ] [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. Bijvoorbeeld:
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).
Conclusie
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)