當資料庫出現性能異常時,如何找出引起性能問題的SQL? SQL Server自帶trace & event只能抓取已執行完成的SQL,且無法抓取SQL運行過程中的狀態信息 通過SQL Server系統視圖可抓取正在運行的SQL和豐富的相關信息,如執行計劃,狀態信息等。將抓取到的數據存放在本地資料庫表 ...
當資料庫出現性能異常時,如何找出引起性能問題的SQL?
- SQL Server自帶trace & event只能抓取已執行完成的SQL,且無法抓取SQL運行過程中的狀態信息
- 通過SQL Server系統視圖可抓取正在運行的SQL和豐富的相關信息,如執行計劃,狀態信息等。將抓取到的數據存放在本地資料庫表中,方便故障分析。
執行相關係統視圖:
sys.dm_exec_requests
sys.dm_exec_sessions
sys.dm_exec_sql_text
sys.dm_exec_query_plan
其他系統視圖:
sys.sysprocesses
sys.dm_db_session_space_usage
系統視圖中信息非常豐富,多抓取一些有用的欄位便於後續的分析工作
各欄位含義詳見官方文檔
具體實現方法:
一、 創建一張表用於存放抓取到的Running SQL及其相關信息
USE [dba_monitor] GO CREATE TABLE [running_sql_monitor]( [id] [int] IDENTITY(1,1) NOT NULL PRIMARY KEY, [Insert_Time] [datetime] NOT NULL DEFAULT (getdate()), [Start_Time] [datetime] NOT NULL, [R_S] [int] NULL, [session_id] [smallint] NOT NULL, [status] [nvarchar](30) NOT NULL, [wait_type] [nvarchar](60) NULL, [wait_resource] [nvarchar](256) NOT NULL, [wait_time] [int] NOT NULL, [cpu_cnt] [int] NULL, [b_spid] [smallint] NULL, [dbname] [nvarchar](128) NULL, [t_level] [smallint] NOT NULL, [o_t_c] [int] NOT NULL, [row_count] [bigint] NOT NULL, [parent_query] [nvarchar](max) NULL, [individual_query] [nvarchar](max) NULL, [QueryPlan_XML] [xml] NULL, [login_name] [nvarchar](128) NOT NULL, [host_name] [nvarchar](128) NULL, [program_name] [nvarchar](128) NULL, [client_interface_name] [nvarchar](32) NULL, [cpu_time] [int] NOT NULL, [logical_reads] [bigint] NOT NULL, [reads] [bigint] NOT NULL, [writes] [bigint] NOT NULL, [memory_usage] [int] NULL, [tempdb_user_objects_mb] [int] NULL, [tempdb_internal_objects_mb] [int] NULL, [login_time] [datetime] NOT NULL, [percent_complete] [real] NOT NULL ) ON [PRIMARY] GO EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'自增列' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'running_sql_monitor', @level2type=N'COLUMN',@level2name=N'id' GO EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'記錄插入時間' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'running_sql_monitor', @level2type=N'COLUMN',@level2name=N'Insert_Time' GO EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'SQL執行開始時間' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'running_sql_monitor', @level2type=N'COLUMN',@level2name=N'Start_Time' GO EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'SQL運行總時間(單位秒)' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'running_sql_monitor', @level2type=N'COLUMN',@level2name=N'R_S' GO EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'SQL使用的CPU核數' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'running_sql_monitor', @level2type=N'COLUMN',@level2name=N'cpu_cnt' GO EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'被哪個session_id阻塞' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'running_sql_monitor', @level2type=N'COLUMN',@level2name=N'b_spid' GO EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'完整的SQL語句' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'running_sql_monitor', @level2type=N'COLUMN',@level2name=N'parent_query' GO EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'正在執行的SQL語句' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'running_sql_monitor', @level2type=N'COLUMN',@level2name=N'individual_query' GO EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'SQL語句的執行計劃' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'running_sql_monitor', @level2type=N'COLUMN',@level2name=N'QueryPlan_XML' GO EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'SQL中的用戶對象占用tempdb大小(單位MB)' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'running_sql_monitor', @level2type=N'COLUMN',@level2name=N'tempdb_user_objects_mb' GO EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'SQL中的內部對象占用tempdb大小(單位MB)' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'running_sql_monitor', @level2type=N'COLUMN',@level2name=N'tempdb_internal_objects_mb' GO
二、創建SQL Server JOB抓取Running SQL
job step1、 抓取Running SQL
INSERT INTO dba_monitor..running_sql_monitor( Start_Time, R_S, session_id, [status], wait_type, wait_resource, wait_time, cpu_cnt, b_spid, DBNAME, t_level, o_t_c, row_count, parent_query, individual_query, QueryPlan_XML, login_name, [host_name], [program_name], client_interface_name, cpu_time, logical_reads, reads, writes, memory_usage, tempdb_user_objects_mb, tempdb_internal_objects_mb, login_time, percent_complete ) SELECT r.start_time, DATEDIFF(s, r.start_time, GETDATE()) AS R_S, r.session_id, r.[status], r.wait_type, r.wait_resource,r.wait_time, x.counts AS cpu_cnt ,r.blocking_session_id AS b_spid, DB_NAME(r.database_id) AS dbname, es.transaction_isolation_level AS t_level,r.open_transaction_count AS o_t_c, es.row_count, parent_query = qt.[text], individual_query = SUBSTRING(qt.[text], (r.statement_start_offset / 2) + 1,((CASE WHEN r.statement_end_offset = -1 THEN LEN(CONVERT(NVARCHAR(MAX), qt.[text])) * 2 ELSE r.statement_end_offset END - r.statement_start_offset) / 2) + 1), QueryPlan_XML = (SELECT query_plan FROM sys.dm_exec_query_plan(r.plan_handle)), es.login_name, es.host_name, es.program_name, es.client_interface_name, r.cpu_time, r.logical_reads, r.reads, r.writes, memory_usage, (su.user_objects_alloc_page_count * 8 /1024) AS tempdb_user_objects_mb, (su.internal_objects_alloc_page_count * 8 /1024) AS tempdb_internal_objects_mb, es.login_time, r.percent_complete FROM sys.dm_exec_requests AS r WITH(NOLOCK) CROSS APPLY sys.dm_exec_sql_text(sql_handle) AS qt INNER JOIN sys.dm_exec_sessions AS es WITH(NOLOCK) ON r.session_id = es.session_id LEFT JOIN (SELECT spid,MAX(loginame)AS loginame,COUNT(0)AS counts FROM sys.sysprocesses WITH(NOLOCK) GROUP BY spid) x ON x.spid=r.session_id LEFT JOIN sys.dm_db_session_space_usage su on es.session_id=su.session_id WHERE es.is_user_process = 1 AND es.session_Id <> @@SPID
job step2、為防止監控表過大,刪除7天前抓取到的數據(請根據實際情況設置JOB運行間隔時間,以及監控數據需要保留的時間周期,避免監控文件過大導致磁碟空間耗盡!!!)
delete top(100) from dba_monitor..running_sql_monitor where Insert_Time < DATEADD(DAY, -7, CAST(GETDATE() as DATE))
分析在出現性能問題時抓取到的SQL,通過執行時長,SQL運行狀態,等待信息來確認哪些SQL是罪魁禍首(部分被抓取到SQL可能是受害者,由於其他SQL占用了的大量系統資源 或 長時間占用鎖資源)
希望能幫助到有需要的同學
本文為原創,轉載請註明:https://www.cnblogs.com/Sylaro0/