Pengantar
Cepat atau lambat, administrator DB ingin memiliki indikator kinerja untuk kueri SQL Server. Seperti yang kita semua tahu, menjalankan Profiler selama 24 jam akan menyebabkan beban sistem yang cukup besar dan oleh karena itu, ini tidak dapat dianggap sebagai solusi optimal untuk database yang digunakan dalam mode 24/7.
Jadi, bagaimana kami bisa mendeteksi status kueri SQL Server? Bagaimana kami dapat menjalankan pelacakan untuk masalah terkait kueri yang terdeteksi tanpa masukan manusia?
Dalam artikel ini, saya akan memberikan implementasi indikator kinerja SQL Server untuk kueri, prosedur tersimpan, dan pemicu, serta penggunaannya untuk penelusuran jejak.
Solusi
Pertama-tama, mari kita lihat pendekatan umum penerapan indikator kinerja untuk kueri, prosedur tersimpan, dan pemicu:
- Pembuatan tabel yang diperlukan untuk pengumpulan dan analisis informasi.
- Pembuatan tampilan untuk pengumpulan informasi.
- Pembuatan prosedur tersimpan untuk pengumpulan informasi.
- Pembuatan tampilan untuk keluaran informasi.
Dan sekarang, mari kita pertimbangkan implementasinya:
1. Pembuatan tabel yang diperlukan untuk pengumpulan dan analisis informasi.
1.1. Untuk pertanyaan:
GUNAKAN [DATABASE_NAME]GOSET ANSI_NULLS ONGOSET QUOTED_IDENTIFIER ONGOSET ANSI_PADDING ONGOCREATE TABLE [srv].[SQL_StatementExecStat]( [ID] [bigint] IDENTITY(1,1) NOT NULL, [datetime]Tanggal] binary](8) NULL, [ExecutionCount] [bigint] NULL, [TotalWorkerTime] [bigint] NULL, [StatementText] [nvarchar](max) NULL, [TotalElapsedTime] [bigint] NULL, CONSTRAINT [PK_SQL_StatementExecStat] PRIMARY KLUSTER [ID] ASC)DENGAN (PAD_INDEX =OFF, STATISTICS_NORECOMPUTE =OFF, IGNORE_DUP_KEY =OFF, ALLOW_ROW_LOCKS =ON, ALLOW_PAGE_LOCKS =ON) ON [PRIMARY]) ON [PRIMARY] TEXTIMAGE_ON [ AN PRIMARY] TEXTIMAGE_ON1.2. Untuk prosedur tersimpan:
GUNAKAN [DATABASE_NAME]GOSET ANSI_NULLS ONGOSET QUOTED_IDENTIFIER ONGOCREATE TABLE [srv].[SQL_ProcedureExecStat]( [ID] [bigint] IDENTITY(1,1) NOT NULL, [InsertDate] [datetime] NULL, [int database_id] NULL, [object_id] [int] NULL, [ExecutionCount] [bigint] NULL, [TotalWorkerTime] [bigint] NULL, [TotalElapsedTime] [bigint] NULL, [TotalPhysicalReads] [bigint] NULL, [TotalLogicalReads] [bi, [TotalLogicalWrites] [bigint] NULL, CONSTRAINT [PK_SQL_ProcedureExecStat] PRIMARY KEY BERKUMPULAN ( [ID] ASC)DENGAN (PAD_INDEX =OFF, STATISTICS_NORECOMPUTE =OFF, IGNORE_DUP_KEY =OFF, ALLOW_WON_MARKON_ROW) PRIMARY]PERGI1.3. Untuk pemicu:
GUNAKAN [DATABASE_NAME]GOSET ANSI_NULLS ONGOSET QUOTED_IDENTIFIER ONGOCREATE TABLE [srv].[SQL_TriggerExecStat]( [ID] [bigint] IDENTITY(1,1) NOT NULL, [InsertDate] [datetime] NULL, [int database_id] NULL, [object_id] [int] NULL, [ExecutionCount] [bigint] NULL, [TotalWorkerTime] [bigint] NULL, [TotalElapsedTime] [bigint] NULL) ON [PRIMARY]GO2. Pembuatan tampilan untuk pengumpulan informasi (di sini kita dapat menyisipkan filter untuk menghilangkan informasi yang tidak relevan (misalnya, kueri dan prosedur dengan pemicu replikasi, dll).
2.1 Untuk kueri:GUNAKAN [DATABASE_NAME]GOSET ANSI_NULLS ONGOSET QUOTED_IDENTIFIER ONGOCREATE tampilan [srv].[vStatementExecInfo] sebagai dengan info sebagai (SELECT query_stats.query_hash AS QueryHash, SUM(query_stats.total_counter_worker_time ) /CPU_time Acution_worker_time ) 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(query_stats.max_work TotalPhysicalReads, MIN(query_stats.min_physical_reads ) AS MinPhysicalReads, MAX(query_stats.max_physical_reads ) AS MaxPhysicalReads, SUM(query_stats.total_physical_reads) / SUM(query_stats.execution_count) AS AvgPhysicalReads, SUM(query_stats.total_logical_writes) AS TotalLogicalWrites, MIN(query_stats.min_logical_writes ) AS MinLogicalWrites, MAX(query_stats.max_logical_writes ) AS MaxLogicalWrites) /statlogical_writesquetotal__statlogical 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_counts.execution_statis. 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 (PILIH QS.query_hash ,QS.total_worker_time ,QS.execution_count ,QS.min_QSworkerphysical_time ,_timeread_physical_time , QS. .total_physical_reads ,QS.total_logical_writes ,QS.min_logical_writes ,QS.max_logical_writes ,QS.min_logical_reads ,QS.max_logical_reads ,QS.total_logical_reads ,QS.min_elapsed_time ,QS.max_elapsed_elapsed_time ,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 - QS.statement_start_offset)/ QS.statement_start_offset 2) + 1) SEBAGAI statement_text DARI sys.dm_exec_query_stats AS QS CROSS APPLY sys.dm_exec_sql_text(QS.sql_handle) as ST) sebagai query_statsWHERE execution_count> 1and last_execution_time>=dateadd(hour,-3,getdate())GROUP BY ) pilih QueryHash, AvgCPU_Time, ExecutionCount, TotalWorkerTime, StatementText, MinWorkerTime, MaxWorkerTime, TotalPhysicalReads, MinPhysicalReads, MaxPhysicalReads, AvgPhysicalReads, TotalLogicalWrites, MinLogicalWrites, MaxLogicalReads, MaxLogicalReads, MaxLogicalReads AvgLogicalReads, TotalElapsedTime, MinElapsedTime, MaxElapsedTime, AvgElapsedTime, MinCreationTime, LastExecuteTimedari infoGODi sini, kueri sistem berikut digunakan:sys.dm_exec_query_stats dan sys.dm_exec_sql_text.
2.2. Untuk prosedur tersimpan:GUNAKAN [DATABASE_NAME]GOSET ANSI_NULLS ONGOSET QUOTED_IDENTIFIER ONGOCREATE lihat [srv].[vProcedureExecInfo] sebagai dengan info sebagai (SELECT procedure_stats.database_id AS database_id, procedure_stats.object_id AS object_id, MIN(procedure_status) .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(procedureText_stats.ProcedureText_stats. (procedure_stats.max_worker_time ) AS MaxWorkerTime, SUM(procedure_stats.total_physical_reads) AS TotalPhysicalReads, MIN(procedure_stats.min_ph ysLogsical_reads ) AS MinPhysicalReads, MAX(procedure_stats.max_physical_reads ) AS MaxPhysicalReads, SUM(procedure_stats.total_physical_reads) / SUM(procedure_stats.execution_count) AS AvgPhysical_writes)AS_LogPhysicalTotal_SUM(procedure_stats. 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 TotalLogical_AS_MIN_read, MIN_LogicalReads SUM(procedure_stats.total_logical_reads ) / SUM(procedure_stats.execution_count) SEBAGAI 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(Timeprocedure_stats. procedure_stats.last_execution_time ) SEBAGAI LastExecuteTimeFROM (PILIH 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_reads_ 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 ROM_execution ,QS. 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)pilih database_id, object_id, type, AvgCPU_Time TotalWorkerTime, ProcedureText, MinWorkerTime, MaxWorkerTime, TotalPhysicalReads, MinPhysicalReads, MaxPhysicalRead s, AvgPhysicalReads, TotalLogicalWrites, MinLogicalWrites, MaxLogicalWrites, AvgLogicalWrites, TotalLogicalReads, MinLogicalReads, MaxLogicalReads, AvgLogicalReads, TotalElapsedTime, MinElapsedTime, MaxElapsedTime info, Di sini, kueri sistem berikut digunakan:sys.dm_exec_Procedure_stats dan sys.dm_exec_sql_text.2.3. Untuk pemicu:
GUNAKAN [DATABASE_NAME]GOSET ANSI_NULLS ONGOSET QUOTED_IDENTIFIER ONGOCREATE lihat [srv].[vTriggerExecInfo] sebagai dengan info sebagai (SELECT procedure_stats.database_id AS database_id, procedure_stats.object_id AS object_id, MIN(procedure_stats) .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(procedureText_stats.ProcedureText_stats. (procedure_stats.max_worker_time ) AS MaxWorkerTime, SUM(procedure_stats.total_physical_reads) AS TotalPhysicalReads, MIN(procedure_stats.min_phys ,WsLogical_reads ) AS MinPhysicalReads, MAX(procedure_stats.max_physical_reads ) AS MaxPhysicalReads, SUM(procedure_stats.total_physical_reads) / SUM(procedure_stats.execution_count) SEBAGAI AvgPhysicalmins)AS_writetotalure_logical_SMIN. 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 TotalLogical_AS_MIN_read, MIN_LogicalReads SUM(procedure_stats.total_logical_reads ) / SUM(procedure_stats.execution_count) SEBAGAI 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.waktu eksekusi. procedure_stats.last_execution_time ) SEBAGAI LastExecuteTimeFROM (PILIH 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_reads_ 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 prosedur QS.teks_waktu QS. LINTAS BERLAKU sys.dm_exec_sql_text(QS.sql_handle) sebagai ST) sebagai procedure_statsWHERE execution_count> 1and last_execution_time>=dateadd(hour,-3,getdate())GROUP BY database_id,object_id)pilih database_id, object_id, type, AvgCPU_Time , ProcedureText, MinWorkerTime, MaxWorkerTime, TotalPhysicalReads, MinPhysicalReads, MaxPhysicalReads, AvgPhysicalReads, TotalLogicalWrites, MinLogicalWrites, MaxLogicalWrites, AvgLogicalWrites, TotalLogicalReads, MinLogicalReads, MaxLogicalReads, AvgLogicalReads, TotalElapsedTime, MinElapsedTime, MaxElapsedTimefrom, Info WaktuTerakhir, Di sini, kueri sistem berikut digunakan:sys.dm_exec_trigger_stats dan sys.dm_exec_sql_text.3. Pembuatan prosedur tersimpan untuk pengumpulan informasi.
3.1. Untuk pertanyaan:
GUNAKAN [DATABASE_NAME]GOSET ANSI_NULLS ONGOSET QUOTED_IDENTIFIER ONGOCREATE PROCEDURE [srv].[InsertForSQL_StatementExecStat] @koef desimal(12,2)=0,0 –koefisien koleksi --dipilih dengan cara eksperimental untuk pengumpulan yang lebih tepat, --di sebagian besar kasus, kita dapat menempatkan 0.0, --jika frekuensi pengumpulan berjalan tidak melebihi 5 menit. --Akurasi perhitungan tergantung pada frekuensi pengumpulan dan koefisien pengumpulan. --Semakin sering pengumpulan berjalan, semakin kecil koefisien pengaruh yang dimiliki.ASBEGIN SET NOCOUNT ON; declare @AvgCPU_Time bigint ,@MaxAvgCPU_Time bigint ,@AvgTotalWorkerTime bigint ,@MaxTotalWorkerTime bigint ,@AvgAvgElapsedTime bigint ,@MaxAvgElapsedTime bigint ,@AvgTotalElapsedTime bigint ,@MaxTotalElapsedTime bigint select @AvgCPU_Time =AVG(AvgCPU_Time), @MaxAvgCPU_Time =max(AvgCPU_Time), @ AvgTotalWorkerTime =AVG(TotalWorkerTime), @MaxTotalWorkerTime =max(TotalWorkerTime), @AvgAvgElapsedTime =AVG(AvgElapsedTime), @MaxAvgElapsedTime =max(AvgElapsedTime), @AvgTotalElapsedTime =AVGTotalElapsedTime =AVG(TotalElapsedTime); masukkan ke srv.SQL_StatementExecStat ( [InsertDate] ,[QueryHash] ,[ExecutionCount] ,[TotalWorkerTime] ,[StatementText] ,[TotalElapsedTime]) pilih getdate() ,[QueryHash] ,[ExecutionCount] ,[TotalWorkerTime] ,[TotalSterTime] ,[TotalElapsedTime] dari srv.vStatementExecInfo di mana(AvgCPU_Time> @AvgCPU_Time + @koef * (@MaxAvgCPU_Time - @AvgCPU_Time)) atau (TotalWorkerTime> @AvgTotalWorkerTime + @koef) (Timetal @MaxTotalWorker) (TimeAxTotalWorker) (@MaxTotalWorker) (@MaxTotalWorker + @koef * (@MaxAvgElapsedTime - @AvgAvgElapsedTime)) atau (TotalElapsedTime> @AvgTotalElapsedTime + @koef * (@MaxTotalElapsedTime - @AvgTotalElapsedTime));ENDGO3.2. Untuk prosedur tersimpan:
GUNAKAN [DATABASE_NAME]GOSET ANSI_NULLS ONGOSET QUOTED_IDENTIFIER ONGOCREATE PROCEDURE [srv].[InsertForProcedureExecStat] @koef desimal(12,2)=0,0 --koefisien koleksi --dipilih dengan cara eksperimental untuk pengumpulan yang lebih tepat, --dalam kebanyakan kasus, kita dapat menempatkan 0,0, -- jika frekuensi pengumpulan berjalan tidak melebihi 5 menit. --Akurasi perhitungan tergantung pada frekuensi pengumpulan dan koefisien pengumpulan. --Semakin sering pengumpulan berjalan, semakin kecil koefisien pengaruh yang dimiliki.ASBEGIN SET NOCOUNT ON; mendeklarasikan @AvgCPU_Time bigint ,@MaxAvgCPU_Time bigint ,@AvgTotalWorkerTime bigint ,@MaxTotalWorkerTime bigint ,@AvgAvgElapsedTime bigint ,@MaxAvgElapsedTime bigint ,@AvgTotalElapsedTime bigTotalElapsedx; pilih @AvgCPU_Time =AVG(AvgCPU_Time), @MaxAvgCPU_Time =max(AvgCPU_Time), @AvgTotalWorkerTime =AVG(TotalWorkerTime), @MaxTotalWorkerTime =max(TotalWorkerTime), @AvgAvgElapsedTime),EvgElapsedTime =AVGTimelapse =AVG(Waktu AvgTotalElapsedTime =AVG(TotalElapsedTime), @MaxTotalElapsedTime =max(TotalElapsedTime) dari srv.vProcedureExecInfo; masukkan ke srv.SQL_ProcedureExecStat ( [InsertDate] ,database_id ,object_id ,[ExecutionCount] ,[TotalWorkerTime] ,[TotalElapsedTime] ,[TotalPhysicalReads] ,[TotalLogicalReads] ,[TotalLogical getWriteid]) pilih ,[TotalWorkerTime] ,[TotalElapsedTime] ,[TotalPhysicalReads] ,[TotalLogicalReads] ,[TotalLogicalWrites] dari srv.vProcedureExecInfo di mana(AvgCPU_Time> @AvgCPU_Time + @koef) @TimerCPU - @Timeer>MaxAvg( @AvTogCPU_Time - @koef *MaxAvg)> koef * (@MaxTotalWorkerTime - @AvgTotalWorkerTime)) atau (AvgElapsedTime> @AvgAvgElapsedTime + @koef * (@MaxAvgElapsedTime - @AvgAvgElapsedTime)) atau (TotalElapsedTime> @AvgTotalElapsedTime + @kotalEl apsedTime - @AvgTotalElapsedTime));ENDGO3.3. Untuk pemicu:
GUNAKAN [DATABASE_NAME]GOSET ANSI_NULLS ONGOSET QUOTED_IDENTIFIER ONGOCREATE PROCEDURE [srv].[InsertForTriggerExecStat] @koef desimal(12,2)=0,0 --koefisien koleksi --dipilih dengan cara eksperimental untuk pengumpulan yang lebih tepat, --dalam kebanyakan kasus, kita dapat menempatkan 0.0, --jika frekuensi pengumpulan berjalan tidak melebihi 5 menit. --Akurasi perhitungan tergantung pada frekuensi pengumpulan dan koefisien pengumpulan. --Semakin sering pengumpulan berjalan, semakin kecil koefisien pengaruh yang dimiliki.ASBEGIN SET NOCOUNT ON; declare @AvgCPU_Time bigint ,@MaxAvgCPU_Time bigint ,@AvgTotalWorkerTime bigint ,@MaxTotalWorkerTime bigint ,@AvgAvgElapsedTime bigint ,@MaxAvgElapsedTime bigint ,@AvgTotalElapsedTime bigint ,@MaxTotalElapsedTime bigint select @AvgCPU_Time =AVG(AvgCPU_Time), @MaxAvgCPU_Time =max(AvgCPU_Time), @ AvgTotalWorkerTime =AVG(TotalWorkerTime), @MaxTotalWorkerTime =max(TotalWorkerTime), @AvgAvgElapsedTime =AVG(AvgElapsedTime), @MaxAvgElapsedTime =max(AvgElapsedTime), @AvgTotalElapsedTime =AVGTotalElapsedTime(TotalElapsedTime) =AVG(); masukkan ke srv.SQL_TriggerExecStat ( [InsertDate] ,database_id ,object_id ,[ExecutionCount] ,[TotalWorkerTime] ,[TotalElapsedTime]) pilih getdate() ,database_id ,object_id ,[ExecutionCount] dariTrigger,[Total.Workertal] di mana(AvgCPU_Time> @AvgCPU_Time + @koef * (@MaxAvgCPU_Time - @AvgCPU_Time)) atau (TotalWorkerTime> @AvgTotalWorkerTime + @koef * (@MaxTotalWorkerTime - @AvgTotalWorkerTime)) atau (AvgElapsedTime> @AvgElapsedTime> @AvgElapsedTime> @AvgElapsedTime> @AvgAvgElapsedTime)) atau (TotalElapsedTime> @AvgTotalElapsedTime + @koef * (@MaxTotalElapsedTime - @AvgTotalElapsedTime));ENDGO4. Pembuatan tampilan untuk keluaran informasi.
4.1. Untuk pertanyaan:
GUNAKAN [DATABASE_NAME]GOSET ANSI_NULLS ONGOSET QUOTED_IDENTIFIER ONGOCREATE VIEW [srv].[vStatementExecTotalInfo]asselect ExecutionCount as Num ,TotalWorkerTime as TotalWorkerTime ,TotalElapsedTime sebagai TotalElapsedTime(100000,CPU_Time ) convert(decimal(8,2),AvgElapsedTime/1000000.) sebagai AvgElapsedSec ,... ,QueryHash ,StatementText from [SRV].[srv].[vStatementExecInfo];GO4.2. Untuk prosedur tersimpan:
GUNAKAN [DATABASE_NAME]GOSET ANSI_NULLS ONGOSET QUOTED_IDENTIFIER ONGOCREATE VIEW [srv].[vProcedureExecTotalInfo]sebagai pilih ExecutionCount sebagai Num ,TotalWorkerTime sebagai TotalWorkerTime ,TotalElapsedTime sebagai TotalElapsedTime ,Segconvert(Avcon)(100000)(Waktu berlalu) ,konversi(desimal(8,2),AvgElapsedTime/1000000.) sebagai AvgElapsedSec ,... ,database_id ,object_id ,db_name(database_id) sebagai DB_Name ,OBJECT_SCHEMA_NAME(object_id, database_id) sebagai Schema_Name(nama_objek,object_name) dari [SRV].[srv].[vProcedureExecInfo];GO4.3. Tampilan untuk pemicu dibuat dengan cara yang sama (jika diperlukan). Bagi saya, saya tidak perlu melacak pemicu, karena jika ada masalah dengan pemicu, eksekusi prosedur tersimpan dan kueri akan menunjukkannya.
Dua parameter berikut sangat penting untuk tampilan yang diimplementasikan:
- AvgWorkerSec — waktu eksekusi kueri dalam hitungan detik.
- AvgElapsedSec — waktu tunggu, atau waktu tunggu+AvgWorkerSec.
Adapun hasil dari pandangan, persamaan berikut ini penting:
AvgWorkerSec=AvgElapsedSec
- AvgWorkerSec>AvgElapsedSec – di sini ada sesuatu yang banyak memuat prosesor pada saat eksekusi kueri (ternyata, pemindaian perangkat lunak antivirus sedang berjalan; mungkin juga karena kesalahan paket yang diparalelkan).
- AvgWorkerSec
Jika AvgWorkerSec=AvgElapsedSec dipatuhi, waktu eksekusi yang lama terkait dengan kueri itu sendiri dan waktu eksekusinya.
Apa kriteria eksekusi kueri yang lama?
Tidak ada jawaban mutlak untuk pertanyaan ini. Itu tergantung pada apa yang dilakukan kueri, di mana dan bagaimana kueri itu digunakan, dll.
Saya memiliki evaluasi berikut untuk kueri ad hoc dan prosedur tersimpan:
- Hingga 0,5 – baik untuk prosedur tersimpan, tidak ada masalah (tidak ada eksekusi yang menunggu).
- Hingga 0,1 – bagus untuk kueri, tidak ada masalah (tidak ada eksekusi yang menunggu).
- 0,5 — 1,0 – buruk untuk prosedur tersimpan, ada masalah (tidak ada eksekusi menunggu yang terlihat oleh pengguna, tetapi masih ada dan memerlukan solusi).
- 0,1 — 0,5 — buruk untuk kueri, ada masalah (tidak ada eksekusi menunggu yang terlihat oleh pengguna, tetapi masih ada dan perlu diselesaikan).
- Lebih dari 1,0 – buruk untuk prosedur tersimpan, ada masalah (ada kemungkinan besar bahwa ada menunggu yang terlihat oleh pengguna, masalah memerlukan solusi segera).
- Lebih dari 0,5 – buruk untuk kueri, ada masalah (kemungkinan besar ada menunggu yang terlihat oleh pengguna, masalah memerlukan solusi segera).
Sedangkan untuk kueri non-ad hoc (unggahan data, pemuatan data), evaluasi di atas dipilih secara individual. Biasanya, ini jauh melebihi evaluasi untuk kueri ad hoc dan prosedur tersimpan.
Jika semua perangkat lunak bekerja melalui prosedur tersimpan, Anda hanya dapat melacak prosedur tersimpan, karena pekerjaan kueri selalu memengaruhi pekerjaan prosedur tersimpan. Itu sebabnya, mari kita selesaikan analisis eksekusi prosedur tersimpan.
Mari kita buat sistem untuk mengumpulkan informasi tentang prosedur tersimpan yang paling berat untuk analisis selanjutnya dan menjalankan autotrace, sesuai dengan algoritme berikut:
1. Pembuatan tabel untuk menyimpan informasi:
GUNAKAN [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, [int] , [OBJECT_ID] [int] NOT NULL, [ExecutionCount] [bigint] NOT NULL, [TotalWorkerTime] [bigint] NULL, [TotalElapsedTime] [bigint] NULL, [Func] [desimal](8, 2) NULL, [AvgWorkerSec ] [desimal](8, 2) NULL, [AvgElapsedSec] [desimal](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] ] [bigint] NULL, [AvgLogicalWrites] [bigint] NULL, [CategoryName] [nvarchar](255) NULL, CONSTRAINT [PK_ SQL_TopProcedureExecStat] PRIMARY KEY CLUSTERED ( [Row_GUID] ASC)DENGAN (PAD_INDEX =OFF, STATISTICS_NORECOMPUTE =OFF, IGNORE_DUP_KEY =OFF, ALLOW_ROW_LOCKS =ON, ALLOW_PAGE_LOCKS) [StatSQL_PAGE_LOCKS] ] ADD CONSTRAINT [DF_SQL_TopProcedureExecStat_Row_GUID] DEFAULT (newid()) UNTUK [Row_GUID]GOALTER TABLE [srv].[SQL_TopProcedureExecStat] TAMBAHKAN CONSTRAINT [DF_SQL_TopProcedureExecStat_SERVER] FORCED_SQLservername) UNTUK DEFAULT [@SecStat_SERVER] (@ TOPProcedureExecStat_SERVER). CONSTRAINT [DF_SQL_TopProcedureExecStat_InsertUTCDate] DEFAULT (getutcdate()) UNTUK [InsertUTCDate]GO2. Pembuatan prosedur tersimpan untuk mengumpulkan informasi:
GUNAKAN [DATABASE_NAME]GOSET ANSI_NULLS ONGOSET QUOTED_IDENTIFIER ONGOCREATE PROCEDURE [srv].[InsertTopProcedureExecStat] @top tinyint=24 – jumlah hari untuk menyimpan catatan ,@CategoryName nvarchar(255)='AvgWorker for selectionBEGIN'AvgWorker'AvgWorker PADA; INSERT INTO [srv].[SQL_TopProcedureExecStat] ([DB_ID] ,[OBJECT_ID] ,[ExecutionCount] ,[TotalWorkerTime] ,[TotalElapsedTime] ,[AvgWorkerSec] ,[AvgElapsedSec] ,[DB_NAME] ,[DB_NAME] , SevCDate ,CategoryName ,TotalPhysicalReads ,TotalLogicalReads ,TotalLogicalWrites ,AvgPhysicalReads ,AvgLogicalReads ,AvgLogicalWrites ) pilih atas(@top) [database_id] ,[object_id]tal ,[Ntal],[Ntal] [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. Misalnya:
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).
Kesimpulan
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)