等待大概分为3类:资源等待、队列等待、外部等待
过滤掉系统相关的等待类型的语句。(查看常用的等待信息)
SELECT wait_type ,
signal_wait_time_ms ,
wait_time_ms
FROM sys.dm_os_wait_stats
WHERE wait_time_ms > 0
AND wait_type NOT IN ( 'CLR_SEMAPHORE', 'CLR_AUTO_EVENT',
'LAZYWRITER_SLEEP', 'RESOURCE_QUEUE',
'SLEEP_TASK', 'SLEEP_SYSTEMTASK',
'SQLTRACE_BUFFER_FLUSH', 'WAITFOR',
'LOGMGR_QUEUE', 'CHECKPOINT_QUEUE',
'REQUEST_FOR_DEADLOCK_SEARCH', 'XE_TIMER_EVENT',
'BROKER_TO_FLUSH', 'BROKER_TASK_STOP',
'CLR_MANUAL_EVENT',
'DISPATCHER_QUEUE_SEMAPHORE',
'FT_IFTS_SCHEDULER_IDLE_WAIT',
'XE_DISPATCHER_WAIT', 'XE_DISPATCHER_JOIN',
'SQLTRACE_INCREMENTAL_FLUSH_SLEEP' )
ORDER BY signal_wait_time_ms DESC
生产环境中使用:
DBCC SQLPERF('sys.dm_os_wait_stats',CLEAR)
当A正在更新一个表并把表锁住时,B也想去读这个表,此时B就必须等待A完成。对于这种情况,可以使用DMV:sys.dm_os_waiting_tasks查看当前正在处于等待状态的会话(sessions),并展示会等待的资源。如果某个会话正在等待某些资源,那么这个DMV结果中的blocking_session_id列就会有这个Session_id
另一个DMV:sys.dm_exec_requests用于返回当前实例上每个用户和内部的连接信息
常见的等待类型:
1.并行执行 2.多任务等待 3.I/O等待 4.备份还原等待 5.锁定等待 6.数据库日志等待 7.外部资源等待
一:并行执行
并行操作最常见的等待类型是CXPACKET
如果是OLTP系统,理想情况下事务很短,就没有必要通过并行运行来提高运行速度了,极端方法,把最大并行度设为1,强制SQL Server 不去使用并行操作,从而减少不必要的资源等待
SELECT *
FROM sys.configurations
WHERE name LIKE '%Max Degree of Parallelism%'
GO
EXEC sys.sp_configure N'show advanced options', N'1'
GO
RECONFIGURE
GO
EXEC sys.sp_configure N'Max Degree of Parallelism', N'1'
GO
RECONFIGURE
如果是OLAP系统,由于事务普遍较长,所以并行操作往往能提高速度和资源利用率。把最大并行度设为0
以下的脚本用于查询计划缓存中存在并行查询的语句
SELECT TOP 10
p.* ,
q.* ,
qs.* ,
cp.plan_handle
FROM sys.dm_exec_cached_plans cp
CROSS APPLY sys.Dm_exec_query_plan(cp.plan_handle) p
CROSS APPLY sys.Dm_exec_sql_text(cp.plan_handle) AS q
JOIN sys.dm_exec_query_stats qs ON qs.plan_handle = cp.plan_handle
WHERE cp.cacheobjtype = 'Compiled Plan'
AND p.query_plan.value('declare namespace p="http://schemas.microsoft.com/SQL Server/2004/07/showplan";
max(//p:RelOp/@Parallel)', 'float') > 0
OPTION ( MAXDOP 1 )
CXPACKET潜在问题:
一个服务器上部署OLTP和OLAP
EXEC sys.sp_configure N'cost threshold for parallelism',N'25'
EXEC sys.sp_configure N'Max Degree of Parallelism',N'2'
RECONFIGURE WITH OVERRIDE --刷新数据库的配置
常见的引起CXPACKET等待类型的情况:
1.在可变类型中数据的分布存在严重倾斜,某列varchar类型的数据,有些数据的长度是60bytes,有些却达到500bytes,对这样的数据进行查询时,会导致执行计划不够高效,也会导致统计信息的可参考性降低。在并行执行时,容易出现某个线程执行很快但另外一个执行很慢的情况,从而增加了CXPACKET的出现概率。
2.在并发的过程中,如果一个线程出现了资源瓶颈,会导致这个线程的速度比其他线程要慢,从而影响整体的运行速度。
3.查询所需要的数据存放在不同的I/O子系统中,而这些子系统的性能又存在差异最终的结果跟上面的第二种情况类似。
4.查询所需要的数据中,不同部分的碎皮不同,所需要的I/O也不同。I/O数量直接影响运行速度和资源开销,甚至影响执行计划的生成,从而也导致了查询过程中不同线程部分的运行速度出现差异。
CXPACKET建议:
1.碎片问题,则减少碎片。
2.从物理文件的存放上要避免出现同一查询的不同部分因为I/O性能差异而出现差异。简单来说就是要保证数据文件所在的盘性能相等
3.尽可能保持统计信息的实时性
4.过多的线程会导致上下文切换开销,也容易引起CXPACkET等待,所以在改动这方面配置时,需要进行严谨的测试和监控
5.绝大部分性能问题的终极方案----优化,从数据库设计、查询编码、索引设计上进行优化
6.最常见的问题是由于查询性能过低,导致SQL Server选择了并行操作,而并行操作又存在一些问题
7.把前面的cost threshold parallelism的值设高
在SQL Server 运行过程中,常常会有不同类型的任务在运行,其中由这种操作产生的等待类型最常见的是SOS_SCHEDULER_YIELD
SOS_SCHEDULER_YIELD等待类型就发生在一个任务自愿放弃当前的资源占用,让给其他任务使用的时候。
用sys.dm_os_sehedulers看看当前有多少runnable的任务在系统中运行
SELECT scheduler_id,current_tasks_count,runnable_tasks_count,work_queue_count,pending_disk_io_count FROM sys.dm_os_schedulers WHERE scheduler_id<255
关注runnable_tasks_count这个列的数据,如果见到长时间存在两位数的数值,意味着CPU可能存在压力,无法应对当前负载。
降低SOS_SCHEDULER_YIELD等待。
查看使用CPU最多的查询语句,针对这些结果来进行优化,还可以查找运行时间最长的脚本进行优化。
SELECT SUBSTRING(qt.text, ( qs.statement_start_offset / 2 ) + 1,
( ( CASE qs.statement_end_offset
WHEN -1 THEN DATALENGTH(qt.text)
ELSE qs.statement_end_offset
END - qs.statement_start_offset ) / 2 ) + 1) ,
qs.execution_count ,
qs.total_logical_reads ,
qs.last_logical_reads ,
qs.total_logical_writes ,
qs.last_logical_writes ,
qs.total_worker_time ,
qs.last_worker_time ,
qs.total_elapsed_time / 1000000 AS total_elapsed_time_in_S ,
qs.last_elapsed_time / 1000000 AS last_elapsed_time_in_S ,
qs.last_execution_time ,
qp.query_plan
FROM sys.dm_exec_query_stats qs
CROSS APPLY sys.dm_exec_sql_text(qs.sql_handle) qt
CROSS APPLY sys.dm_exec_query_plan(qs.sql_handle) qp
ORDER BY qs.total_worker_time DESC --CPU时间
检查 活动事务的日志情况脚本。
SELECT DTST.[session_id] ,
DES.[login_name] AS [Login Name] ,
DB_NAME(DTDT.database_id) AS [Database] ,
DTDT.[database_transaction_begin_time] AS [Begin Time] ,
-- DATEDIFF(ms,DTDT.[database_transaction_begin_time], GETDATE()) AS [Durationms],
CASE DTAT.transaction_type
WHEN 1 THEN 'Read/write'
WHEN 2 THEN 'Read-only'
WHEN 3 THEN 'System'
WHEN 4 THEN 'Distributed'
END AS [Transaction Type] ,
CASE DTAT.transaction_state
WHEN 0 THEN 'Not fully initialized'
WHEN 1 THEN 'Initialized, not started'
WHEN 2 THEN 'Active'
WHEN 3 THEN 'Ended'
WHEN 4 THEN 'Commit initiated'
WHEN 5 THEN 'Prepared, awaiting resolution'
WHEN 6 THEN 'Committed'
WHEN 7 THEN 'Rolling back'
WHEN 8 THEN 'Rolled back'
END AS [Transaction State] ,
DTDT.[database_transaction_log_record_count] AS [Log Records] ,
DTDT.[database_transaction_log_bytes_used] AS [Log Bytes Used] ,
DTDT.[database_transaction_log_bytes_reserved] AS [Log Bytes RSVPd] ,
DEST.[text] AS [Last Transaction Text] ,
DEQP.[query_plan] AS [Last Query Plan]
FROM sys.dm_tran_database_transactions DTDT
INNER JOIN sys.dm_tran_session_transactions DTST ON DTST.[transaction_id] = DTDT.[transaction_id]
INNER JOIN sys.[dm_tran_active_transactions] DTAT ON DTST.[transaction_id] = DTAT.[transaction_id]
INNER JOIN sys.[dm_exec_sessions] DES ON DES.[session_id] = DTST.[session_id]
INNER JOIN sys.dm_exec_connections DEC ON DEC.[session_id] = DTST.[session_id]
LEFT JOIN sys.dm_exec_requests DER ON DER.[session_id] = DTST.[session_id]
CROSS APPLY sys.dm_exec_sql_text(DEC.[most_recent_sql_handle]) AS DEST
OUTER APPLY sys.dm_exec_query_plan(DER.[plan_handle]) AS DEQP
ORDER BY DTDT.[database_transaction_log_bytes_used] DESC;
-- ORDER BY [Duration ms] DESC;