网上看到这篇文章挺不错的,直接翻译过来。
在尝试诊断SQL Server性能时,不要仅仅依赖某个单一的诊断数据,比如CPU的使用率、SQL Server磁盘性能,就得出结论却忽略的问题的根源。实际上,使用单一的度量经常会得出一个错误的诊断。
在SQL Server中CPU、IO和内存的使用是相互依赖。在我们采取"knee-jerk"修正行动(添加内存、提升磁盘吞吐量或者更改配置设置)之前,我们需要先查看整体情况。
一、服务器级别
"进程X运行缓慢,你能修复它吗?"
作为DBA,你决定打开资源监视器(resmon),发现磁盘活动异常

在性能监视器(perfmon)查看部分磁盘相关的计数器

伴随着磁盘请求队列的平均长度急剧上升,从磁盘读取数据存在较高的延迟。好像磁盘子系统成为一个瓶颈,它正努力跟上请求数量,SQL Server磁盘IO性能恶化,队列形成。
你应该采取什么行动解决这个问题呢?在你打算联系系统管理员去看是否可以做些什么来提升磁盘能力,你最好是再深入挖掘下。
如果你只关注一个特定的性能指标,很容易对SQL Server性能问题进行误诊。
你可能有证据表明存在一个特定的瓶颈,但是在本例SQL Server磁盘IO,把磁盘性能不够或者磁盘子系统配置当成问题的根源就有点危险了。
The excessive disk IO could easily have its root cause elsewhere.你需要收集更多的数据来确认问题的根源。但是,你从哪里开始呢?
二、深入挖掘
2.1、Wait statistics
一个好的开始是分析SQL Server等待统计,它通常能够表明为什么SQL Server会话在获取所需资源以执行请求前被迫暂停处理(等待)。通过分析等待,我们可以识别繁忙的服务器上主要引起争用的资源。
查询sys.dm_os_waiting_tasks动态管理视图(DMV),显示所有正在等待资源执行的语句,可以关联其他DMVs得到相关会话和请求的详细信息。作为演示,我们执行以下脚本

--Analyze the queries that are currently running or which have recently ran and their plan is still in the cache.
SELECT dm_ws.wait_duration_ms,
dm_ws.wait_type,
dm_es.status,
dm_t.TEXT,
dm_qp.query_plan,
dm_ws.session_ID,
dm_es.cpu_time,
dm_es.memory_usage,
dm_es.logical_reads,
dm_es.total_elapsed_time,
dm_es.program_name,
DB_NAME(dm_r.database_id) DatabaseName,
-- Optional columns
dm_ws.blocking_session_id,
dm_r.wait_resource,
dm_es.login_name,
dm_r.command,
dm_r.last_wait_type
FROM sys.dm_os_waiting_tasks dm_ws
INNER JOIN sys.dm_exec_requests dm_r ON dm_ws.session_id = dm_r.session_id
INNER JOIN sys.dm_exec_sessions dm_es ON dm_es.session_id = dm_r.session_id
CROSS APPLY sys.dm_exec_sql_text (dm_r.sql_handle) dm_t
CROSS APPLY sys.dm_exec_query_plan (dm_r.plan_handle) dm_qp
WHERE dm_es.is_user_process = 1
GO
--You can change CROSS APPLY to OUTER APPLY if you want to see all the details which are omitted because of the plan cache

我们看到两个等待类型,以及相关的查询文本和计划

当然,我们经常对一个问题进行回顾性调查,这样前面的DMV就没太大的用处。作为替换,我们可以查询sys.dm_os_wait_stats动态管理视图,它包含所有等待类型的"运行总值",自上次服务重启或者我们使用DBCC SQLPERF命令手动重置统计信息以来所有请求的累积值

--All wait types since the server was last restarted, or the statistics were manually reset
WITH [Waits] AS
(SELECT
[wait_type],
[wait_time_ms] / 1000.0 AS [WaitS],
([wait_time_ms] - [signal_wait_time_ms]) / 1000.0 AS [ResourceS],
[signal_wait_time_ms] / 1000.0 AS [SignalS],
[waiting_tasks_count] AS [WaitCount],
100.0 * [wait_time_ms] / SUM ([wait_time_ms]) OVER() AS [Percentage],
ROW_NUMBER() OVER(ORDER BY [wait_time_ms] DESC) AS [RowNum]
FROM sys.dm_os_wait_stats
WHERE [wait_type] NOT IN (
N'BROKER_EVENTHANDLER', N'BROKER_RECEIVE_WAITFOR',
N'BROKER_TASK_STOP', N'BROKER_TO_FLUSH',
N'BROKER_TRANSMITTER', N'CHECKPOINT_QUEUE',
N'CHKPT', N'CLR_AUTO_EVENT',
N'CLR_MANUAL_EVENT', N'CLR_SEMAPHORE', -- Maybe uncomment these four if you have mirroring issues
N'DBMIRROR_DBM_EVENT', N'DBMIRROR_EVENTS_QUEUE',
N'DBMIRROR_WORKER_QUEUE', N'DBMIRRORING_CMD', N'DIRTY_PAGE_POLL', N'DISPATCHER_QUEUE_SEMAPHORE',
N'EXECSYNC', N'FSAGENT',
N'FT_IFTS_SCHEDULER_IDLE_WAIT', N'FT_IFTSHC_MUTEX', -- Maybe uncomment these six if you have AG issues
N'HADR_CLUSAPI_CALL', N'HADR_FILESTREAM_IOMGR_IOCOMPLETION',
N'HADR_LOGCAPTURE_WAIT', N'HADR_NOTIFICATION_DEQUEUE',
N'HADR_TIMER_TASK', N'HADR_WORK_QUEUE', N'KSOURCE_WAKEUP', N'LAZYWRITER_SLEEP',
N'LOGMGR_QUEUE', N'MEMORY_ALLOCATION_EXT',
N'ONDEMAND_TASK_QUEUE',
N'PREEMPTIVE_XE_GETTARGETSTATE',
N'PWAIT_ALL_COMPONENTS_INITIALIZED',
N'PWAIT_DIRECTLOGCONSUMER_GETNEXT',
N'QDS_PERSIST_TASK_MAIN_LOOP_SLEEP', N'QDS_ASYNC_QUEUE',
N'QDS_CLEANUP_STALE_QUERIES_TASK_MAIN_LOOP_SLEEP',
N'QDS_SHUTDOWN_QUEUE', N'REDO_THREAD_PENDING_WORK',
N'REQUEST_FOR_DEADLOCK_SEARCH', N'RESOURCE_QUEUE',
N'SERVER_IDLE_CHECK', N'SLEEP_BPOOL_FLUSH',
N'SLEEP_DBSTARTUP', N'SLEEP_DCOMSTARTUP',
N'SLEEP_MASTERDBREADY', N'SLEEP_MASTERMDREADY',
N'SLEEP_MASTERUPGRADED', N'SLEEP_MSDBSTARTUP',
N'SLEEP_SYSTEMTASK', N'SLEEP_TASK',
N'SLEEP_TEMPDBSTARTUP', N'SNI_HTTP_ACCEPT',
N'SP_SERVER_DIAGNOSTICS_SLEEP', N'SQLTRACE_BUFFER_FLUSH',
N'SQLTRACE_INCREMENTAL_FLUSH_SLEEP',
N'SQLTRACE_WAIT_ENTRIES', N'WAIT_FOR_RESULTS',
N'WAITFOR', N'WAITFOR_TASKSHUTDOWN',
N'WAIT_XTP_RECOVERY',
N'WAIT_XTP_HOST_WAIT', N'WAIT_XTP_OFFLINE_CKPT_NEW_LOG',
N'WAIT_XTP_CKPT_CLOSE', N'XE_DISPATCHER_JOIN',
N'XE_DISPATCHER_WAIT', N'XE_TIMER_EVENT')
AND [waiting_tasks_count] > 0
)
SELECT
MAX ([W1].[wait_type]) AS [WaitType],
CAST (MAX ([W1].[WaitS]) AS DECIMAL (16,2)) AS [Wait_S],
CAST (MAX ([W1].[ResourceS]) AS DECIMAL (16,2)) AS [Resource_S],
CAST (MAX ([W1].[SignalS]) AS DECIMAL (16,2)) AS [Signal_S],
MAX ([W1].[WaitCount]) AS [WaitCount],
CAST (MAX ([W1].[Percentage]) AS DECIMAL (5,2)) AS [Percentage],
CAST ((MAX ([W1].[WaitS]) / MAX ([W1].[WaitCount])) AS DECIMAL (16,4)) AS [AvgWait_S],
CAST ((MAX ([W1].[ResourceS]) / MAX ([W1].[WaitCount])) AS DECIMAL (16,4)) AS [AvgRes_S],
CAST ((MAX ([W1].[SignalS]) / MAX ([W1].[WaitCount])) AS DECIMAL (16,4)) AS [AvgSig_S],
CAST ('https://www.sqlskills.com/help/waits/' + MAX ([W1].[wait_type]) as XML) AS [Help/Info URL]
FROM [Waits] AS [W1]
INNER JOIN [Waits] AS [W2]
ON [W2].[RowNum] <= [W1].[RowNum]
GROUP BY [W1].[RowNum]
HAVING SUM ([W2].[Percentage]) - MAX( [W1].[Percentage] ) < 95; -- percentage threshold
GO


重置sys.dm_os_wait_stats统计信息

-- reset statistics
DBCC SQLPERF (N'sys.dm_os_wait_stats', CLEAR);
GO

当前查询的两个最贵的等待类型ASYNC_NETWORK_IO和PAGEIOLATCH_SH也出现在这里。如果我们调查的问题不是一个经常性或长期存在的问题,那么有可能相关的等待类型将被"淹没"在历史的等待之中。这就是为什么维护等待统计基线是很重要的。
ASYNC_NETWORK_IO等待类型是一个网络相关的等待,表明客户端不能足够快地处理数据(例子中的查询重重执行并返回大量数据给SSMS)。In general, watch out for this wait type as a warning of inefficient data processing on the client-side.
例子中更有趣的是PAGEIOLATCH_SH等待类型,意味着查询请求必须等待一个latch,以便从磁盘中读取没有保存在buffer cache中的数据。这似乎加强了我们最初的直觉,问题在磁盘供电或错误的配置上。如果磁盘子系统无法快速返回页面,那么它可能会导致更长的请求等待队列来获取页面,以及latch争用。
然而,如果我们怀疑磁盘瓶颈,什么是根本原因?一个有用的下一步是找出更多的IO负载。比如,磁盘活动是否有特别的数据库和数据库文件"热点"?
2.2、Virtual file statistics
通过查询sys.dm_io_virtual_file_stats动态管理视图我们可以知道文件使用统计信息。使用如下脚本

-- Get I/O utilization by database (Query 31) (IO Usage By Database)
WITH Aggregate_IO_Statistics
AS
(SELECT DB_NAME(database_id) AS [Database Name],
CAST(SUM(num_of_bytes_read + num_of_bytes_written)/1048576 AS DECIMAL(12, 2)) AS io_in_mb
FROM sys.dm_io_virtual_file_stats(NULL, NULL) AS [DM_IO_STATS]
GROUP BY database_id)
SELECT ROW_NUMBER() OVER(ORDER BY io_in_mb DESC) AS [I/O Rank], [Database Name], io_in_mb AS [Total I/O (MB)],
CAST(io_in_mb/ SUM(io_in_mb) OVER() * 100.0 AS DECIMAL(5,2)) AS [I/O Percent]
FROM Aggregate_IO_Statistics
ORDER BY [I/O Rank] OPTION (RECOMPILE);
-- Helps determine which database is using the most I/O resources on the instance

它提供洞察数据库实例上的IO活动,特别是用于揭示IO负载是否分布在不同的数据库

上图显示SQLMonTest数据库是IO热点。同样,这些统计数据是自服务上次启动以来的累积值,但不同于等待统计,不能手动重置(The DMF contains cumulative I/O information for each database file. The DMF data get reset on the instance restart, database offline, detached and reattached activities.)。下面的脚本能够明确的显示相关数据库的高读和写延迟。AdventureWorks2014和SQLMonTest出现在列表的前面

-- 下面的查询返回哪个数据库文件有最大的I/O延迟
-- Calculates average stalls per read, per write,
-- and per total input/output for each database file.
SELECT DB_NAME(fs.database_id) AS [database_name],
mf.physical_name,
CONVERT(DECIMAL(18, 2), mf.size / 128.0) AS [file_size_mb],
io_stall_read_ms,
num_of_reads,
CAST(io_stall_read_ms / (1.0 + num_of_reads) AS NUMERIC(10, 1)) AS [avg_read_stall_ms],
io_stall_write_ms,
num_of_writes,
CAST(io_stall_write_ms / (1.0 + num_of_writes) AS NUMERIC(10, 1)) AS [avg_write_stall_ms],
io_stall_read_ms + io_stall_write_ms AS [io_stalls],
num_of_reads + num_of_writes AS [total_io],
CAST((io_stall_read_ms + io_stall_write_ms) / (1.0 + num_of_reads + num_of_writes) AS NUMERIC(10, 1)) AS [avg_io_stall_ms]
FROM sys.dm_io_virtual_file_stats(NULL, NULL) AS fs
INNER JOIN sys.master_files AS mf WITH (NOLOCK)
ON fs.database_id = mf.database_id
AND fs.[file_id] = mf.[file_id]
ORDER BY avg_io_stall_ms DESC OPTION(RECOMPILE);
-- Helps determine which database files on
-- the entire instance have the most I/O bottlenecks

I/O Latency


2.3、Query execution statistics
已经把问题缩小到一个或两个数据库,下一个逻辑问题是:哪些请求引起数据库上的IO活动?我们可以检查sys.dm_exec_query_stats中的查询执行统计信息。我们可以使用下面的脚本得到指定数据库中总物理读最高的查询以及相关的plan handles(输出结果的部分列省略)

SELECT TOP 10
t.text ,
execution_count ,
statement_start_offset AS stmt_start_offset ,
sql_handle ,
plan_handle ,
total_logical_reads / execution_count AS avg_logical_reads ,
total_logical_writes / execution_count AS avg_logical_writes ,
total_physical_reads / execution_count AS avg_physical_reads,t.dbid
FROM sys.dm_exec_query_stats AS s
CROSS APPLY sys.dm_exec_sql_text(s.sql_handle) AS t
--WHERE DB_NAME(t.dbid) = 'AdventureWorks2008R2'
ORDER BY avg_physical_reads DESC;


我们可以将结果中的plan_handle代入sys.dm_exec_query_plan得到查询的执行计划,再去分析是否存在潜在问题。
总结
通过整体审查所有相关的性能指标,我们有最好的机会选择最有效的行动。在本例中,很明显最初的努力方向应该是查询优化和索引策略,因为我们的查询没有WHERE筛选,导致SQL Server执行整表联接。过度的页面读取导致"缓存流失",SQL Server刷新缓冲页到磁盘用于容纳新页,其后果就是"磁盘瓶颈",and sessions waiting to obtain buffer cache latches on pages that need to be read from disk.

Resolving SQL Server Disk IO bottlenecks的更多相关文章

  1. Performance Monitor4:监控SQL Server的IO性能

    SQL Server的IO性能受到物理Disk的IO延迟和SQL Server内部执行的IO操作的影响.在监控Disk性能时,最主要的度量值(metric)是IO延迟,IO延迟是指从Applicati ...

  2. 虚拟机备份克隆导致SQL SERVER 出现IO错误案例

      案例环境:   服务器配置: CPU: Intel E5-2690  RAM: 12G   虚拟机 操作系统  : Windows Server 2008 R2 Standard Edtion   ...

  3. VITAM POST MORTEM – ANALYZING DEADLOCKED SCHEDULERS MINI DUMP FROM SQL SERVER

    https://gennadny.wordpress.com/2014/11/ Since SQL Server 7.0, SQL Server has its own scheduling mech ...

  4. SQL Server 服务器磁盘测试之SQLIO篇(一)

    数据库调优工作中,有一部分是需要排查IO问题的,例如IO的速度或者RAID级别无法响应高并发下的快速请求.最常见的就是查看磁盘每次读写的响应速度,通过性能计数器Avg.Disk sec/Read(Wr ...

  5. Disk IO Performance

    一,使用 Performance counter 监控Disk IO问题 1,Physical Disk vs. Logical Disk Windows可以在一个Physical Disk上划出若干 ...

  6. 转载 50种方法优化SQL Server数据库查询

    原文地址 http://www.cnblogs.com/zhycyq/articles/2636748.html 50种方法优化SQL Server数据库查询 查询速度慢的原因很多,常见如下几种: 1 ...

  7. SQL Server 服务器磁盘测试之SQLIO篇

    原文:SQL Server 服务器磁盘测试之SQLIO篇 数据库调优工作中,有一部分是需要排查IO问题的,例如IO的速度或者RAID级别无法响应高并发下的快速请求.最常见的就是查看磁盘每次读写的响应速 ...

  8. SQL Server 服务器磁盘测试之SQLIO篇(二)

    上次放出了一篇文章,针对磁盘卷簇大小默认4KB和自定义64KB进行了测试,测试内容为随机和顺序读写,大小为8KB和64KB,有人觉得这并没有照顾到SQL Server所有的IO使用情景.这篇测试文章, ...

  9. SQL Server索引进阶:第四级,页和区

    原文地址: Stairway to SQL Server Indexes: Level 4, Pages and Extents 本文是SQL Server索引进阶系列(Stairway to SQL ...

随机推荐

  1. 【Java EE 学习 45】【Hibernate学习第二天】【对象的三种状态】【一对多关系的操作】

    一.对象的三种状态. 1.对象有三种状态:持久化状态.临时状态.脱管状态(游离状态) 2.Session的特定方法能使得一个对象从一个状态转换到另外一个状态. 3.三种状态的说明 (1)临时状态:临时 ...

  2. Knockout.js随手记(1)

    新的开始,knockout.js 1.首先去http://knockoutjs.com/index.html下载knockout.js,最新的版本是2.3 2.知道什么是Knockout?它是个Jav ...

  3. Alpha 测试

    活动助手Alpha--测试篇 测试分工 人员 分工 测试 牛姐 Android开发/ui设计 功能测试 橙汁 Android开发 功能测试 洪 数据库开发 数据库结构测试 佳凯 数据库设计与开发 接口 ...

  4. 北京电子科技学院(BESTI)实验报告3

    北京电子科技学院(BESTI)实验报告3 课程: 信息安全系统设计基础 班级:1452.1453 姓名:(按贡献大小排名)周恩德 .郑凯杰 学号:(按贡献大小排名)20145217 .201453 指 ...

  5. MySQL 存储过程控制语句

    变量作用域内部的变量在其作用域范围内享有更高的优先权,当执行到end.变量时,内部变量消失,此时已经在其作用域外,变量不再可见了,应为在存储过程外再也不能找到这个申明的变量,但是你可以通过out参数或 ...

  6. 【Oracle】oracle之listagg分析函数

    oracle分析函数——listagg篇 (1)使用listagg将多行数据合并到一行 例表: select deptno, ename from emp order by deptno, ename ...

  7. Leetcode Edit Distance

    Given two words word1 and word2, find the minimum number of steps required to convert word1 to word2 ...

  8. ZeroMQ实例-使用ZMQ(ZeroMQ)进行局域网内网络通信

    本文内容摘要:1)安装zeromq.2)实例说明使用zmq进行网络间的消息发送和接收 首先在机器中安装zmq库 步骤如下: 1)下载zeromq的源代码,ZeroMQ的官方网址:http://zero ...

  9. Linux安装软件总结(二.几种安装命令介绍)

    一.rpm包安装方式步骤: 1.找到相应的软件包,比如soft.version.rpm,下载到本机某个目录: 2.打开一个终端,su -成root用户: 3.cd soft.version.rpm所在 ...

  10. 01 LabVIEW的类中各个Scope的范围

    范例地址: D:\Program Files (x86)\National Instruments\LabVIEW 2015\examples\Object-Oriented Programming\ ...