Предговор
Рано или късно администраторът на DB би искал да има индикатор за ефективност за заявки на SQL Server. Както всички знаем, пускането на Profiler за 24 часа ще доведе до значително натоварване на системата и следователно не може да се счита за оптимално решение за бази данни, използвани в денонощен режим.
И така, как можем да открием състоянието на заявките на SQL Server? Как можем да изпълним трасиране за открити проблеми, свързани със заявката, без човешкия вход?
В тази статия ще предоставя реализация на индикатора за производителност на SQL Server за заявки, съхранени процедури и тригери, както и използването му за изпълнение на проследяването.
Решение
Първо, нека да разгледаме общия подход към прилагането на индикатора за ефективност за заявки, съхранени процедури и тригери:
- Създаване на необходими таблици за събиране и анализ на информация.
- Създаване на изглед за събиране на информация.
- Създаване на съхранени процедури за събиране на информация.
- Създаване на изглед за извеждане на информация.
И сега, нека разгледаме реализацията:
1. Създаване на необходими таблици за събиране и анализ на информация.
1.1. За запитвания:
ИЗПОЛЗВАЙТЕ [DATABASE_NAME]GOSET ANSI_NULLS ONGOSET QUOTED_IDENTIFIER ONGOSET ANSI_PADDING ONGOCREATE TABLE [srv].[SQL_StatementExecStat]( [ID] [bigint] IDENTITY(1,1) InHste NULL, [da NOT NULL,Da] binary](8) NULL, [ExecutionCount] [bigint] NULL, [TotalWorkerTime] [bigint] NULL, [StatementText] [nvarchar](max) NULL, [TotalElapsedTime] [bigint] NULL, CONSTRAINT [PK_SQL_STERYMATTA] [ID] ASC)С (PAD_INDEX =OFF, STATISTICS_NORECOMPUTE =OFF, IGNORE_DUP_KEY =OFF, ALLOW_ROW_LOCKS =ON, ALLOW_PAGE_LOCKS =ON) ВКЛ.1.2. За съхранени процедури:
ИЗПОЛЗВАЙТЕ [DATABASE_NAME]GOSET ANSI_NULLS ONGOSET QUOTED_IDENTIFIER ONGOCREATE TABLE [srv].[SQL_ProcedureExecStat]( [ID] [bigint] IDENTITY(1,1) NOT NULL, [InsertDate] [date_id] [date] NULL, [object_id] [int] NULL, [ExecutionCount] [bigint] NULL, [TotalWorkerTime] [bigint] NULL, [TotalElapsedTime] [bigint] NULL, [TotalPhysicalReads] [bigint] NULL, [TotalLogicalReads]] NULL [TotalLogicalWrites] [bigint] NULL, ОГРАНИЧЕНИЕ [PK_SQL_ProcedureExecStat] ПЪРВИЧЕН КЛУСТРИРАН ( [ID] ASC) С (PAD_INDEX =ИЗКЛЮЧЕНО, STATISTICS_NORECOMPUTE =ИЗКЛЮЧЕНО, IGNORE_DUP_KEY =OFF, OFF, LOONAGEPORAKS) [OFF, LOONAGEPORAKS] PRIMARY]GO1.3. За тригери:
ИЗПОЛЗВАЙТЕ [DATABASE_NAME]GOSET ANSI_NULLS ONGOSET QUOTED_IDENTIFIER ONGOCREATE TABLE [srv].[SQL_TriggerExecStat]( [ID] [bigint] IDENTITY(1,1) NOT NULL, [InsertDate ] [date_id] [date] NULL, [object_id] [int] NULL, [ExecutionCount] [bigint] NULL, [TotalWorkerTime] [bigint] NULL, [TotalElapsedTime] [bigint] NULL) НА [PRIMARY]GO2. Създаване на изглед за събиране на информация (тук можем да вмъкнем филтри за премахване на неуместната информация (например заявки и процедури с тригери за репликация и т.н.).
2.1. За заявки:ИЗПОЛЗВАЙТЕ [DATABASE_NAME]GOSET ANSI_NULLS ONGOSET QUOTED_IDENTIFIER ONGOCREATE изглед [srv].[vStatementExecInfo] като с информация като (SELECT query_stats.query_hash КАТО QueryHash, SUM(query_stats.total) SUM(query_stats.total) SPU (query_stats.total) SPU (време_на_работа) 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_stats.ASQueryxвреме_прочитане_SUM. 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_counts. Като avgphysicalreads, sum (query_stats.total_logical_writes) като totallogicwrites, min (query_stats.min_logical_writes) като minlogicalwrites, max (query_stats.max_logical_writes) като maxlogilwrites, sum_stats.total_logical_writes) / query_stations.tery_stution). 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 ) / Total_logical_reads ) / Total_logical_reads ) / Total_logical_reads ) / Total_logical_reads ) / Totals. 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.execution_count,QS.execution_count,QS.execution_count,QS.execution_count,QS.execution_count,QS.execution_count,QS.execution_count,QS.execution_count,QS.execution_count,QS.execution_count,QS.execution_count,QS.execution_count,QS.execution_count,QS.execution_count,QS.min. .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_logical_writes ,QS. ,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_/ QStstart_offset END_/ 2) + 1) AS statement_text FROM sys.dm_exec_query_stats КАТО QS КРЪСТО ПРИЛОЖИ sys.dm_exec_sql_text(QS.sql_handle) като ST) като query_statsWHERE execution_count> 1and last_execution_time>=our date.query_hag ) изберете QueryHash, AvgCPU_Time, ExecutionCount, TotalWorkerTime, StatementText, MinWorkerTime, MaxWorkerTime, TotalPhysicalReads, MinPhysicalReads, MaxPhysicalReads, AvgPhysicalReads, TotalLogicalWrites, TotalLogicalWrites, MinLogicalReads, MinLogicalReads, MinLogicalReads, MinLogicalReads, MinLogicalReads AvgLogicalReads, TotalElapsedTime, MinElapsedTime, MaxElapsedTime, AvgElapsedTime, MinCreationTime, LastExecuteTime от infoGOТук се използват следните системни заявки:sys.dm_exec_query_stats и sys.dm_exec_sql_text.
2.2. За съхранени процедури:ИЗПОЛЗВАЙТЕ [DATABASE_NAME]GOSET ANSI_NULLS ONGOSET QUOTED_IDENTIFIER ONGOCREATE изглед [srv].[vProcedureExecInfo] като с информация като (SELECT procedure_stats.database_id AS database_id, procedure_stats.object_id AS object_id, MIN.stats_proced AS object_id, MIN.proced_proced AS(type_proced.proced) .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.execution_count) Min(procedure_stats.execution_count) AS Mine(procedure_stats.total_worker_time) AS (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_stats.total_logical_writes) AS TotalLogicalWrites, MIN(procedure_stats.min_logical_writes ) AS MinLogicalWrites, MAX( 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_logical_reads ) AS MinLogicalReads, MAX(procedure_stats.max_logical_reads ) AS MaxLogicalReads, 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.total_elapsed_time ) / SUM(procedure_stats.total_elapsed_time ) / SUM(procedure_stats.total_elapsed_time ) / SUM(procedure_stats.total_elapsed_time ) / SUM(procedure_stats.total_elapsed_time). 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.min_worker_time,QS.min_worker_time,QS.min_worker_time,QS.min_worker_time,QS.min_worker_time,QS.min_worker_time,QS.min_worker_time,QS.max. 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) като ST) като procedure_statsWHERE execution_count> 1 и last_execution_time>=dateadd(hour,-3,getdate())GROUP BY database_id,object_id)select_id database_id,object_id, object_idcu database_id, object_idcu database_type TotalWorkerTime, ProcedureText, MinWorkerTime, MaxWorkerTime, TotalPhysicalReads, MinPhysicalReads, MaxPhysicalRead s, AvgPhysicalReads, TotalLogicalWrites, MinLogicalWrites, MaxLogicalWrites, AvgLogicalWrites, TotalLogicalReads, MinLogicalReads, MaxLogicalReads, AvgLogicalReads, TotalElapsedTime, MinElapsedTime, MinElapsedTime, MaxgElapsedTime, MaxgElapsedTime,MaxElapsedTime,MaxElapsedTime,MaxElapsedTime,AvgElapsedCueInfo>Тук се използват следните системни заявки:sys.dm_exec_Procedure_stats и sys.dm_exec_sql_text.
2.3. За тригери:
ИЗПОЛЗВАЙТЕ [DATABASE_NAME]GOSET ANSI_NULLS ONGOSET QUOTED_IDENTIFIER ONGOCREATE изглед [srv].[vTriggerExecInfo] като с информация като (SELECT procedure_stats.database_id AS database_id, procedure_stats.object_id AS object_id, MIN.stats_proced AS тип_обект, MIN.proced_proced .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.execution_count) Min(procedure_stats.execution_count) AS Mine(procedure_stats.total_worker_time) AS (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.total_logical_writes) AS TotalLogicalWrites, MIN(procedure_stats.min_logical_writes ) AS MinLogicalWrites, MAX( 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_logical_reads ) AS MinLogicalReads, MAX(procedure_stats.max_logical_reads ) AS MaxLogicalReads, 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 AvgElapsedTime, MIN(procedure_stats.cached_time ) AS MinCachedTime, MAX( 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.min_worker_time,QS.min_worker_time,QS.min_worker_time,QS.min_worker_time,QS.min_worker_time,QS.min_worker_time,QS.min_worker_time,QS.max. 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 КРЪСТО ПРИЛОЖИ sys.dm_exec_sql_text(QS.sql_handle) като ST) като procedure_statsWHERE брой_изпълнение> 1 и последно_време на_изпълнение>=dateadd(hour,-3,getdate())GROUP BY database_id,object_id)select object_id database_id,object_Time)select objectWPU type_id,AvC_type database_id,AvC , ProcedureText, MinWorkerTime, MaxWorkerTime, TotalPhysicalReads, MinPhysicalReads, MaxPhysicalReads, AvgPhysicalReads, TotalLogicalWrites, MinLogicalWrites, MaxLogicalWrites, AvgLogicalWrites, TotalLogicalReads, MinLogicalReads, MaxLogicalReads, AvgLogicalReads, TotalElapsedTime, MinElapsedTime, MaxElapsedTime, MaxElapsedTime, MaxElapsedTime, AvgElapsedTime, AvgLogicalWrites, TotalLogicalReads, MinLogicalReads, MaxLogicalReads, AvgLogicalReads, TotalElapsedTime, MinElapsedTime, MaxElapsedTime, MaxElapsedTime, MaxElapsedTime, AvgLogicalWrites, MinLogicalReads, MinLogicalReads, MaxLogicalReads, AvgLogicalReads, TotalElapsedTime, MinElapsedTime, MaxElapsedTime, MaxElapsedTime, MaxElapsedTime, AvgClapsedTime, AvgТук се използват следните системни заявки:sys.dm_exec_trigger_stats и sys.dm_exec_sql_text.
3. Създаване на съхранени процедури за събиране на информация.
3.1. За запитвания:
ИЗПОЛЗВАЙТЕ [DATABASE_NAME]GOSET ANSI_NULLS ONGOSET QUOTED_IDENTIFIER ONGOCREATE PROCEDURE [srv].[InsertForSQL_StatementExecStat] @koef decimal(12,2)=0.0 – коефициент на събиране, избира се по-точен експериментален начин – се избира за повече в най-точната колекция в колекцията. случаи, можем да поставим 0.0, --ако честотата на събиране не надвишава 5 минути. --Точността на изчислението зависи от честотата на събиране и коефициента на събиране. --Колкото по-често се изпълнява събирането, толкова по-малко влияние оказва коефициентът на събиране.ASBEGIN SET NOCOUNT ON; деклариране @avgcpu_time bigint,@maxavgcpu_time bigint,@avgtotalworkertime bigint,@maxtotalworkertime bigint,@avgavlapsedtime bigint,@maxavgelapsed bigint,@avgtotalelapsedtime bigint,@maxtotalelaped bigint select @vgccpu_time AvgTotalWorkerTime =AVG(TotalWorkerTime), @MaxTotalWorkerTime =max(TotalWorkerTime), @AvgAvgElapsedTime =AVG(AvgElapsedTime), @MaxAvgElapsedTime =max(AvgElapsedTime), @AvgElapsedTime =max(AvgElapsedTime), @AvgElapsedTime =max(AvgElapsedTime), @AvgTotalWorkerTime =max(TotalWorkerTime); вмъкнете в srv.SQL_StatementExecStat ( [InsertDate] ,[QueryHash] ,[ExecutionCount] ,[TotalWorkerTime] ,[StatementText] ,[TotalElapsedTime]) изберете getdate() ,[TotalElapsedTime],[TotalElapsedTime],[TotalElapsedTime],[TotalElapsedTime],[TotalElapsedTime],[TotalElapsedTime],[TotalElapsedTime],[TotalElapsedTime],[TotalHash],[Execution],[Execution Count],ort Count[Execution],T] ,[TotalElapsedTime] от srv.vStatementExecInfo, където(AvgCPU_Time> @AvgCPU_Time + @koef * (@MaxAvgCPU_Time - @AvgCPU_Time)) или (TotalWorkerTime> @AvgTotalWorkerTime + @Tops @Tops @VgEvThеm * (@MaxElapsedTime) + @koef * (@MaxAvgElapsedTime - @AvgAvgElapsedTime)) или (TotalElapsedTime> @AvgTotalElapsedTime + @koef * (@MaxTotalElapsedTime - @AvgTotalElapsedTime));ENDGO3.2. За съхранени процедури:
ИЗПОЛЗВАЙТЕ [DATABASE_NAME]GOSET ANSI_NULLS ONGOSET QUOTED_IDENTIFIER ONGOCREATE PROCEDURE [srv].[InsertForProcedureExecStat] @koef decimal(12,2)=0.0 --коефициент на събиране --се избира по точен експериментален начин, --в колекция повече. в повечето случаи можем да поставим 0.0, -- ако честотата на събиране не надвишава 5 минути. --Точността на изчислението зависи от честотата на събиране и коефициента на събиране. --Колкото по-често се изпълнява събирането, толкова по-малко влияние оказва коефициентът на събиране.ASBEGIN SET NOCOUNT ON; декларира @AvgCPU_Time bigint ,@MaxAvgCPU_Time bigint ,@AvgTotalWorkerTime bigint ,@MaxTotalWorkerTime bigint ,@AvgAvgElapsedTime bigint ,@MaxAvgElapsedTime bigint ,@AvgTimes bigTotalElapsed bigTotalEla; изберете @AvgCPU_Time =AVG(AvgCPU_Time), @MaxAvgCPU_Time =max(AvgCPU_Time), @AvgTotalWorkerTime =AVG(TotalWorkerTime), @MaxTotalWorkerTime =max(TotalWorkerTime), @AvgAvgElapsedTime(TotalWorkerTime), @AvgAvgElapsedTime(AvgElapsedGxv) AvgTotalElapsedTime =AVG(TotalElapsedTime), @MaxTotalElapsedTime =max(TotalElapsedTime) от srv.vProcedureExecInfo; insert into srv.SQL_ProcedureExecStat ( [InsertDate] ,database_id ,object_id ,[ExecutionCount] ,[TotalWorkerTime] ,[TotalElapsedTime] ,[TotalPhysicalReads] ,[TotalLogicalReads] ,[TotalLogicalWrites]) select getdate() ,database_id ,object_id ,[ExecutionCount] ,[TotalWorkerTime] ,[TotalElapsedTime] ,[TotalPhysicalReads] ,[TotalLogicalReads] ,[TotalLogicalWrites] от srv.vProcedureExecInfo, където (AvgCPU_Time> @AvgCPU_Time + @koef @koef * или) @Tog ToW * (@Max_Time +Time) koef * (@MaxTotalWorkerTime - @AvgTotalWorkerTime)) или (AvgElapsedTime> @AvgAvgElapsedTime + @koef * (@MaxAvgElapsedTime - @AvgAvgElapsedTime)) или (TotalElapsedTime> @koAvgElapsedTime> @AvgElapsedTime +@koef * apsedTime - @AvgTotalElapsedTime));ENDGO3.3. За тригери:
ИЗПОЛЗВАЙТЕ [DATABASE_NAME]GOSET ANSI_NULLS ONGOSET QUOTED_IDENTIFIER ONGOCREATE PROCEDURE [srv].[InsertForTriggerExecStat] @koef decimal(12,2)=0.0 --коефициент на събиране --се избира по точен експериментален начин, --за повече колекция. в повечето случаи можем да поставим 0.0, --ако честотата на събиране не надвишава 5 минути. --Точността на изчислението зависи от честотата на събиране и коефициента на събиране. --Колкото по-често се изпълнява събирането, толкова по-малко влияние оказва коефициентът на събиране.ASBEGIN SET NOCOUNT ON; деклариране @avgcpu_time bigint,@maxavgcpu_time bigint,@avgtotalworkertime bigint,@maxtotalworkertime bigint,@avgavlapsedtime bigint,@maxavgelapsed bigint,@avgtotalelapsedtime bigint,@maxtotalelaped bigint select @vgccpu_time AvgTotalWorkerTime =AVG(TotalWorkerTime), @MaxTotalWorkerTime =max(TotalWorkerTime), @AvgAvgElapsedTime =AVG(AvgElapsedTime), @MaxAvgElapsedTime =max(AvgElapsedTime), @AvgElapsedTime, @AvgElapsedTime, @AvgElapsedTime, @AvgElapsedTime =max(AvgElapsedTime), @AvgElapsedTime =max(AvgElapsedTime), @AvgTotalWorkerTime =max(TotalWorkerTime), @AvgAvgElapsedTime =AVG(AvgElapsedTime), @MaxAvgElapsedTime =max(AvgElapsedTime), @AvgElapsedTime =max(AvgElapsedTime), @AvgElapsedTime =max(AvgElapsedTime), @AvgTotalWorkerTime =max(TotalWorkerTime); вмъкнете в srv.SQL_TriggerExecStat ( [InsertDate] ,database_id ,object_id ,[ExecutionCount] ,[TotalWorkerTime] ,[TotalElapsedTime]) изберете getdate() ,database_id ,object_id ,[Execution_Time][ExecutionTorvPs] където(AvgCPU_Time> @AvgCPU_Time + @koef * (@MaxAvgCPU_Time - @AvgCPU_Time)) или (TotalWorkerTime> @AvgTotalWorkerTime + @koef * (@MaxTotalWorkerTime - @AvgTotalWorkerTime - @AvgTotalWorkerTime @AvgTotalWorkerTime @koef)) или (AvgTotalWorkerTime @Avg *LavgAvg +AvgElaps) @AvgAvgElapsedTime)) или (TotalElapsedTime> @AvgTotalElapsedTime + @koef * (@MaxTotalElapsedTime - @AvgTotalElapsedTime));ENDGO4. Създаване на изглед за изхода на информация.
4.1. За запитвания:
ИЗПОЛЗВАЙТЕ [DATABASE_NAME]GOSET ANSI_NULLS ONGOSET QUOTED_IDENTIFIER ONGOCREATE VIEW [srv].[vStatementExecTotalInfo]изберете ExecutionCount като Num ,TotalWorkerTime като TotalWorkerTime ,TotalElapsedTime,TotalElapsedTime,TotalElapsedTime,TotalElapsedTime,TotalElapsedTime,AvtotalElapsedTime,AvtotalElapsedTime като TotalElapsedTime,StotalElapsedTime като TotalElapsedTime,Av000000000000000000000000 000 000% convert(decimal(8,2),AvgElapsedTime/1000000.) като AvgElapsedSec ,... ,QueryHash ,StatementText от [SRV].[srv].[vStatementExecInfo];GO4.2. За съхранени процедури:
ИЗПОЛЗВАЙТЕ [DATABASE_NAME]GOSET ANSI_NULLS ONGOSET QUOTED_IDENTIFIER ONGOCREATE VIEW [srv].[vProcedureExecTotalInfo]както изберете ExecutionCount като Num ,TotalWorkerTime като TotalWorkerTime ,TotalElapsedTime as TotalWorkerTime,TotalElapsedTime as TotalWorkerTime,TotalElapsedTime as TotalWorkerTime,TotalElapsedTime as TotalWorkerTime,TotalElapsedTime as Total. ,convert(decimal(8,2),AvgElapsedTime/1000000.) като AvgElapsedSec ,... ,database_id ,object_id ,db_name(database_id) като DB_Name ,OBJECT_SCHEMA_NAME(object_id) като Schidobject_id_на_обект,_ime_на_обект_на_обект_на_обект_на_обект_на_обект_на_обект_на_обект_на_обект от [SRV].[srv].[vProcedureExecInfo];GO4.3. Изгледите за тригери се създават по подобен начин (ако е необходимо). Що се отнася до мен, нямам нужда от тригери за проследяване, тъй като ако има някакви проблеми с тригерите, изпълнението на съхранени процедури и заявки ще ги покаже.
Следните два параметъра имат жизненоважно значение за внедрените изгледи:
- AvgWorkerSec — време за изпълнение на заявката в секунди.
- AvgElapsedSec — време на изчакване или време на изчакване+AvgWorkerSec.
Що се отнася до резултатите от изгледите, следното равенство е важно:
AvgWorkerSec=AvgElapsedSec
- AvgWorkerSec>AvgElapsedSec – тук нещо натоварва силно процесора в момента на изпълнение на заявката (както се оказа, сканирането на антивирусния софтуер е стартирано; може да е по вина и на паралелния план).
- AvgWorkerSec
Ако се спазва AvgWorkerSec=AvgElapsedSec, дългото време за изпълнение е свързано със самата заявка и времето за нейното изпълнение.
Какъв е критерият за изпълнение на дълга заявка?
Няма абсолютен отговор на този въпрос. Зависи какво прави заявката, къде и как се използва и т.н.
Имам следната оценка за ad hoc заявките и съхранените процедури:
- До 0,5 – добре за съхранени процедури, няма проблеми (без изчакване на изпълнение).
- До 0.1 – добре за заявки, без проблеми (без изчакване на изпълнение).
- 0.5 — 1.0 – лошо за съхранените процедури, има проблеми (няма изчаквания на изпълнение, които да се виждат от потребителя, но те все още съществуват и изискват решение).
- 0,1 — 0,5 — лошо за заявки, има проблеми (няма изчаквания за изпълнение, които да се виждат от потребителя, но те все още съществуват и изискват решаване).
- Повече от 1.0 – лошо за съхранените процедури, има проблеми (има голяма вероятност да има изчаквания, които са видими за потребителите, проблемът изисква незабавно решение).
- Повече от 0,5 – лошо за заявки, има проблеми (има голяма вероятност да има изчаквания, които са видими за потребителите, проблемът изисква незабавно решение).
Що се отнася до заявките, които не са ad hoc (качване на данни, зареждане на данни), горната оценка се избира на индивидуална основа. Обикновено той значително надвишава оценките за ad hoc заявки и съхранени процедури.
Ако целият софтуер работи чрез съхранените процедури, можете да проследите само съхранените процедури, тъй като работата на заявките винаги влияе върху работата на съхранените процедури. Ето защо, нека се спрем на анализа на изпълнението на съхранените процедури.
Нека създадем система за събиране на информация за най-тежките съхранени процедури за последващ анализ и стартиране на autotrace, съгласно следния алгоритъм:
1. Създаване на таблица за съхранение на информация:
ИЗПОЛЗВАЙТЕ [DATABASE_NAME]GOSET ANSI_NULLS ONGOSET QUOTED_IDENTIFIER ONGOCREATE TABLE [srv].[SQL_TopProcedureExecStat]( [Row_GUID] [уникален идентификатор] NOT NULL, [SERVER] [nvardb] [nvardb] [nvardb] , [OBJECT_ID] [int] NOT NULL, [ExecutionCount] [bigint] NOT NULL, [TotalWorkerTime] [bigint] NULL, [TotalElapsedTime] [bigint] NULL, [Func] [десетично](8, 2) NULL, [SecgWorker ] [десетична](8, 2) NULL, [AvgElapsedSec] [десетична](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] [AvgPhysicalReads] [AvgPhysicalReads] ] [bigint] NULL, [AvgLogicalWrites] [bigint] NULL, [CategoryName] [nvarchar](255) NULL, CONSTRAINT [PK_ SQL_TOPPROCEDUREEXECSTAT] първичен ключ, струпван ([row_guid] asc) с (pad_index =off, statistics_noreCompute =off, ignore_dup_key =off, releme_row_locks =on, releme_page_locks =on) on [първичен]) на [първичен] таблица на вратата [SRV]. [Sql_topproced ] Добавяне на ограничение [df_sql_topprocedureexecstat_row_guid] по подразбиране (newid ()) за [row_guid] таблица на вратата [srv]. [SQL_TOPPROCEDUREEXECSTAT] Добавяне на ограничение [df_sql_topprocedureexecstat_server] по подразбиране (@@servername) за [server] table table_server]. ОГРАНИЧЕНИЕ [DF_SQL_TopProcedureExecStat_InsertUTCDate] ПО ПОДРАЗБИРАНЕ (getutcdate()) ЗА [InsertUTCDate]GO2. Създаване на съхранена процедура за събиране на информация:
ИЗПОЛЗВАЙТЕ [DATABASE_NAME]GOSET ANSI_NULLS ONGOSET QUOTED_IDENTIFIER ONGOCREATE PROCEDURE [srv].[InsertTopProcedureExecStat] @top tinyint=24 – брой дни за съхраняване на записи ,@CategoryName nvarchar(2CategoryName nvarchar) или SET за селекция от NOCAV – 25 за COUNT категория –25VgW) НА; INSERT INTO [srv].[SQL_TopProcedureExecStat] ([DB_ID] ,[OBJECT_ID] ,[ExecutionCount] ,[TotalWorkerTime] ,[TotalElapsedTime] ,[AvgWorkerSec] ,[AvgElapsedSec] ,[AvgElapsedSec],[AvgElapsedSec],[JELApsedSec],[JELApsedSec],[JELApsedSec],[JELApsedSec],[JELApsedSec] ,[JELApsedSec] InsertUTCDate ,CategoryName ,TotalPhysicalReads ,TotalLogicalReads ,TotalLogicalWrites ,AvgPhysicalReads ,AvgLogicalReads ,AvgLogicalWrites) select top(@top) [database_id] ,[object_id] ,[Num] ,[TotalWorkerTime] ,[TotalElapsedTime] ,[AvgWorkerSec] ,[AvgElapsedSec] , [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. Например:
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).
Заключение
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)