性能调优是DBA的重要工做之一。不少人会带着各类性能上的问题来问咱们。咱们须要经过SQL Server知识来处理这些问题。常常被问到的一个问题是:早上这个存储过程运行时间仍是能够的,但到了晚上就很慢很慢。对此,咱们能够笑着回答:这个存储过程运行屡次后,已经累趴了,因此很慢。sql
存储过程或语句运行时间取决于服务器的工做量。若是在晚上,服务器负担很重的话,你的存储过程可能须要更多的时间来运行,由于它在等待CPU周期(CPU cycle)和IO完成(IO completion)。为了得到一致的响应时间,咱们须要减小执行完成的资源需求,那就是所谓的性能调优。缓存
IO和CPU是完成执行的主要资源使用对象。更少的资源使用,更稳定的性能表现。这篇文章咱们来理解下性能调优中DBCC STATISTCS IO所扮演的角色。服务器
默认状况下SET STATISTCS IO是停用的,咱们能够经过下列语句在当前会话级别打开。ide
1 SET STATISTICS IO ON
这个语句能够帮助咱们得到在语句执行时,所发生IO数(页读/写)。咱们来看一个例子的输出。sqlserver
1 USE StatisticsDB 2 GO 3 SELECT * INTO SalesOrderDetail FROM AdventureWorks2008R2.Sales.SalesOrderDetail 4 GO 5 SET STATISTICS IO ON 6 DBCC dropcleanbuffers 7 DBCC freeproccache 8 GO 9 SELECT * FROM SalesOrderDetail 10 GO 11 SELECT * FROM SalesOrderDetail
Set Statistics IO的输出信息能够在消息TAB页里找到。一样的语句咱们执行了2次,第一次是在清空缓存后执行,第2次没有。性能
咱们来看下输出信息:优化
根据微软在线帮助,扫描计数是在任何方向都达到叶级别后启动的查询/扫描数,目的在于检索用于构造输出的最终数据集的全部值。ui
当您使用对非主键列定义的非惟一的汇集索引搜索一个值时,扫描计数为 1。 这是为了针对您正在搜索的键值检查重复值。 例如 WHERE Clustered_Index_Key_Column = <value>。 spa
当 N 为经过使用索引键定位键值后,在叶级别的左侧或右侧启动的不一样查找/扫描数时,则扫描计数为 N。3d
这个数字告诉咱们优化器所选择的计划,对这个对象的重复读取次数。不少人误觉得这个是对整张表的读取次数,这是彻底错误的。
咱们经过一个例子来理解扫描计数。
1 CREATE TABLE ScanCount (Id INT IDENTITY(1,1),Value CHAR(1)) 2 INSERT INTO ScanCount (Value ) VALUES ('A') ,('B'),('C'),('D'), ('E') , ('F') 3 CREATE UNIQUE CLUSTERED INDEX ix_ScanCount ON ScanCount(Id) 4 5 SET STATISTICS IO ON 6 --Unique clustered Index used to search single value 7 SELECT * FROM ScanCount WHERE Id =1 8 --Unique clustered Index used to search multiple value 9 SELECT * FROM ScanCount WHERE Id IN(1,2,3,4,5,6) 10 --Unique clustered Index used to search multiple value 11 SELECT * FROM ScanCount WHERE Id BETWEEN 1 AND 6
咱们来看下上面3个查询语句的输出。
在第1个SELECT语句的输出里,扫描计数为0。这和MSDN里在线帮助“若是使用的索引是主键的惟一索引或汇集索引而且您仅查找一个值,则扫描计数为 0。”描述一致。由于它是惟一索引(汇集/非汇集索引),不须要在叶子层,进行进一步的向左或向右扫描,由于这里只有一个值来匹配。那也是在惟一索引上查找单一值,扫描计数为0的缘由。扫描计数是1的话,会在非惟一索引(汇集或非汇集索引)上发生。
对于第2个SELECT语句,扫描计数是6.这是由于咱们在找多个不一样值。MSDN在线帮助对此有详细说明: “若是使用的索引是主键的惟一索引或非汇集索引,你在查找N个值,则扫描计数为N。”。
咱们来看看执行计划里的SEEK谓语,将更清晰:
即便只有一个where条件,仍是会分裂成多个谓语。对于每一个SEEK谓语,它会生成1个扫描数。
对于最后一个SELECT语句,扫描计数为1,由于MSDN在线帮助说了: “当 N 为经过使用索引键定位键值后,在叶级别的左侧或右侧启动的不一样查找/扫描数时,则扫描计数为 N。” 在叶子节点汇集索引结构用来找到1值后,叶子层的向左扫描开始,直到找到值6。咱们看下执行计划里的SEEK 谓语,将更清晰:
从数据缓存读取的页数。数字越小,性能越好。在性能调优中这个数字很是重要。由于它不会随着执行又执行而改变,除非数据或查询语句有变更。在进行性能调优时,这个能够做为性能提高的重要参考。
从磁盘读取的页数。这个会随着执行又执行而改变。大多数状况下,连续第2次的执行时,它的物理读取值为0(能够参考上面连续查询的物理读取数变化)。
若是连续执行后,物理读取次数降低了,咱们能够假定是服务器上内存使用配置的错误,或者服务器工做量饱和,有内存压力。你须要在服务器级别思考问题的缘由。在查询调优时,这个数字不过重要,由于它一直在变,对于降低这个值,你不能对它作出太多控制。
为进行查询而放入缓存的页数。这个值告诉咱们物理页读取数,即SQL Server执行的,做为预读机制的一部分。在查询执行请求那些可能用到页以前,SQL Server把物理数据页读入缓存,用于完成接下来查询的页须要。
能够看到,物理读取是2次,预读是946次。这就是说,查询执行请求了2个页,并预读了946个页到数据缓存,SQL Server估计下次查询可能要用到这些页。和物理读取同样,这个值对在查询调优里并不重要。
从数据缓存读取的 text、ntext、image 或大值类型 (varchar(max)、nvarchar(max)、varbinary(max)) 页的数目。这个和逻辑读同样重要,咱们要很是重视。
从磁盘读取的 text、ntext、image 或大值类型页的数目。
为进行查询而放入缓存的 text、ntext、image 或大值类型页的数目。
总结下,逻辑读取和LOB逻辑读取是2个重要数值,在性能调优时,咱们要重点围观。若是把这2个值调低,不在本文的讨论范围。一般建立合适的索引或重写查询能够帮助咱们完全下降这2个值。