在初涉SQL Server性能問題(1/4)裏,咱們知道了如何快速檢查服務器實例上正運行的任務數和IO等待的任務數。這個是輕量級的腳本,不會給服務器形成任何壓力,即便服務器在高負荷下,也能夠正常得到結果。html
問題檢測的第2步是獲取在進行任何資源等待的會話。下面的腳本會幫助咱們得到這些信息。這個查詢須要預創建一個函數,若是會話是由SQL Server代理啓動的話,會顯示具體的做業名。node
1 /***************************************************************************************** 2 PREREQUISITE FUNCTION 3 ******************************************************************************************/ 4 USE MASTER 5 GO 6 CREATE FUNCTION ConvertStringToBinary ( @hexstring VARCHAR(100) 7 ) RETURNS BINARY(34) AS 8 BEGIN 9 10 RETURN(SELECT CAST('' AS XML).value('xs:hexBinary( substring(sql:variable("@hexstring"), sql:column("t.pos")) )', 'varbinary(max)') 11 FROM (SELECT CASE SUBSTRING(@hexstring, 1, 2) WHEN '0x' THEN 3 ELSE 0 END) AS t(pos)) 12 END 13 /*************************************************************************************** 14 STEP 2: List the session which are currently waiting for resource 15 ****************************************************************************************/ 16 SELECT node.parent_node_id AS Node_id, 17 es.HOST_NAME, 18 es.Login_name, 19 CASE WHEN es.program_name LIKE '%SQLAgent - TSQL JobStep%' THEN 20 ( 21 SELECT 'SQL AGENT JOB: '+name FROM msdb..sysjobs WHERE job_id= 22 MASTER.DBO.ConvertStringToBinary (LTRIM(RTRIM((SUBSTRING(es.program_name,CHARINDEX('(job',es.program_name,0)+4,35))))) 23 ) 24 ELSE es.program_name END AS [Program Name] , 25 DB_NAME(er.database_id) AS DatabaseName, 26 er.session_id, 27 wt.blocking_session_id, 28 wt.wait_duration_ms, 29 wt.wait_type, 30 wt.NoThread , 31 er.command, 32 er.status, 33 er.wait_resource, 34 er.open_transaction_count, 35 er.cpu_time, 36 er.total_elapsed_time AS ElapsedTime_ms, 37 er.percent_complete , 38 er.reads, 39 er.writes, 40 er.logical_reads, 41 wlgrp.name AS ResoursePool , 42 SUBSTRING (sqltxt.TEXT,(er.statement_start_offset/2) + 1, 43 ((CASE WHEN er.statement_end_offset = -1 44 THEN LEN(CONVERT(NVARCHAR(MAX), sqltxt.TEXT)) * 2 45 ELSE er.statement_end_offset 46 END - er.statement_start_offset)/2) + 1) AS [Individual Query], 47 sqltxt.TEXT AS [Batch Query] 48 FROM (SELECT session_id, SUM(wait_duration_ms) AS 49 wait_duration_ms,wait_type,blocking_session_id,COUNT(*) AS NoThread 50 FROM SYS.DM_OS_WAITING_TASKS GROUP BY session_id, wait_type,blocking_session_id) wt 51 INNER JOIN SYS.DM_EXEC_REQUESTS er ON wt.session_id=er.session_id INNER JOIN SYS.DM_EXEC_SESSIONS es ON es.session_id= er.session_id 52 INNER JOIN SYS.DM_RESOURCE_GOVERNOR_WORKLOAD_GROUPS wlgrp ON wlgrp.group_id=er.group_id 53 INNER JOIN (SELECT os.parent_node_id ,task_address FROM SYS.DM_OS_SCHEDULERS OS INNER JOIN 54 SYS.DM_OS_WORKERS OSW ON OS.scheduler_address=OSW.scheduler_address 55 WHERE os.status='VISIBLE ONLINE' GROUP BY os.parent_node_id ,task_address ) node 56 ON node.task_address=er.task_address 57 CROSS APPLY SYS.DM_EXEC_SQL_TEXT(er.sql_handle) AS sqltxt 58 WHERE sql_handle IS NOT NULL AND wt.wait_type NOT IN ('WAITFOR','BROKER_RECEIVE_WAITFOR') 59 GO
輸出結果的每列說明介紹以下:sql
上述查詢屢次執行後,輸出結果有很長wait_duration_ms的會話,這個會話不被其餘會話阻塞,且一直在輸出結果裏。咱們就要看看這個會話的程序名,主機名,登陸用戶名,還有對應的執行語句,具體進行什麼操做形成的。根據這些信息,咱們能夠選擇性的去終止這個會話,而後分析下具體的執行語句。若是會話是被阻塞的,咱們要用另外的語句來找出阻塞的會話。數據庫
第3步,列出服務器上正運行的會話清單。服務器
1 /*************************************************************************************** 2 STEP 3: List the session which are currently waiting/running 3 ****************************************************************************************/ 4 SELECT node.parent_node_id AS Node_id, 5 es.HOST_NAME, 6 es.login_name, 7 CASE WHEN es.program_name LIKE '%SQLAgent - TSQL JobStep%' THEN 8 (SELECT 'SQL AGENT JOB: '+name FROM msdb..sysjobs WHERE job_id=DBO.ConvertStringToBinary (LTRIM(RTRIM((SUBSTRING(es.program_name,CHARINDEX('(job',es.program_name,0)+4,35))))) 9 )ELSE es.program_name END AS program_name , 10 DB_NAME(er.database_id) AS DatabaseName, 11 er.session_id, 12 wt.blocking_session_id, 13 wt.wait_duration_ms, 14 wt.wait_type, 15 wt.NoThread , 16 er.command, 17 er.status, 18 er.wait_resource, 19 er.open_transaction_count, 20 er.cpu_time, 21 er.total_elapsed_time AS ElapsedTime_ms, 22 er.percent_complete , 23 er.reads,er.writes,er.logical_reads, 24 wlgrp.name AS ResoursePool , 25 SUBSTRING (sqltxt.TEXT,(er.statement_start_offset/2) + 1, 26 ((CASE WHEN er.statement_end_offset = -1 27 THEN LEN(CONVERT(NVARCHAR(MAX), sqltxt.TEXT)) * 2 28 ELSE er.statement_end_offset 29 END - er.statement_start_offset)/2) + 1) AS [Individual Query], 30 sqltxt.TEXT AS [Batch Query] 31 FROM 32 SYS.DM_EXEC_REQUESTS er INNER JOIN SYS.DM_EXEC_SESSIONS es ON es.session_id= er.session_id 33 INNER JOIN SYS.DM_RESOURCE_GOVERNOR_WORKLOAD_GROUPS wlgrp ON wlgrp.group_id=er.group_id 34 INNER JOIN (SELECT os.parent_node_id ,task_address FROM SYS.DM_OS_SCHEDULERS OS 35 INNER JOIN SYS.DM_OS_WORKERS OSW ON OS.scheduler_address=OSW.scheduler_address 36 WHERE os.status='VISIBLE ONLINE' GROUP BY os.parent_node_id ,task_address ) node ON node.task_address=er.task_address 37 LEFT JOIN 38 (SELECT session_id, SUM(wait_duration_ms) AS 39 wait_duration_ms,wait_type,blocking_session_id,COUNT(*) AS NoThread 40 FROM SYS.DM_OS_WAITING_TASKS GROUP BY session_id, wait_type,blocking_session_id) wt 41 ON wt.session_id=er.session_id 42 CROSS apply SYS.DM_EXEC_SQL_TEXT(er.sql_handle) AS sqltxt 43 WHERE sql_handle IS NOT NULL AND ISNULL(wt.wait_type ,'') NOT IN 44 ('WAITFOR','BROKER_RECEIVE_WAITFOR') 45 ORDER BY er.total_elapsed_time DESC 46 47 GO
這裏的輸出列和第2步徹底相同,我會分析total_elapsed_time佔用時間較長的會話,酌情考慮是否終止這些會話,並分析下對應的執行SQL語句。大多數狀況下(服務器一致運行穩定,忽然卡住了),使用上述步驟就能夠解決問題。下一篇文章咱們會看下阻塞的會話,還有打開未活動事務的會話。 session