SQLServer数据库优化与管理——等待篇

SQL Server 2008 R2有490种等待状态(SQL Server 2014 CTP1中甚至多达759种),
但是常用的、有价值的其实不多。总的来说,等待类型分为3类:资源等待、队列等待和
外部等待。在日常使用中,通常会过滤掉系统相关的等待类型,因为这些对诊断性能瓶颈
没有多大用处,同时还过滤掉等待时间为0的类型,语句如下

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

在数据库配置方面,首先要考虑的是,如果是OLTP系统,理想情况下事务很短,这
时候就没有必要通过并行运行来提高运行速度了。所以对于这类系统,有一个极端方法(如
非必要不要用),即把最大并行度(Max Degree of Parallelism)设为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
GO

如果是OLAP系统,由于事务普遍较长,所以并行操作往往能提高速度和资源利用率。
这时候可以让SQL Server自己控制并行,也就是把最大并行度设为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 )

正如前面所述,这个等待通常与CPU压力有关,最简单的解决方案就是增加CPU或
者提升CPU性能。不过通常服务器也不是随便可以改的,所以不应该为了解决问题而解决
问题,要多考虑其产生的原因。当查询需要大面积扫描时,会消耗CPU资源。.下面的脚本
可以查看使用最多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时间

常见降低 WRITELOG等待的手段是把日志文件和数据文件及其他文件如TEMPDB存
放到独立的磁盘中。另外就是避免类似游标等的低效操作,同时加快提交事务的频率,最
后检查I/O相关的计数器。
除此之外,删除没用的非聚集索引、减少日志开销、修改索引键或使用填充因子减少
页分裂(第6章介绍过)、修改程序架构、把负载分摊到多个服务器或者数据库中,这些手
段都能减少出现这类等待的情况。
3.扩充说明
不要见到这种等待就以为是I/O问题,也不要直接增加日志文件。上面已经说过,
增加日志文件解决不了这类问题。
应该进行如下更加深入的分析:
查看sys.dm_.io_virtual_file_stats的数据。
查看LOGBUFFER等待(下面介绍),看是否存在对日志缓冲区(log buffer)的争抢。
查看日志文件所在磁盘的磁盘等待队列。
查看事务的平均大小。
查看是否有大量的页分裂,因为这样也会导致大量的日志。,
下面的脚本用于检查活动事务的日志情况。

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;

你可能感兴趣的:(数据库调优,数据库,sql,server,数据库)