在考虑采取优化行动之前(比如添加索引或非范式化),应该知道当前的查询是怎样被处理的,还应该有一些性能测量基线,这样才能比较改动前后的性能。SQL Server提供了一些工具(SET 选项)来支持对查询性能的监测;
在执行查询前启用SET选项,他们会产生相应的输出。SQL Server Management Studio的工具|选项中可以设置IO统计和时间统计的开关。
也可以通过下面语句实现相同的功能。
SET STATISTICS IO ON GO SET STATISTICS TIME ON GO
注意:开启统计之后,查询结果需以文本格式显示才能看见统计;
IO统计
“IO统计”这个选项统计了SQL Server为处理查询做了多少工作。当这个选项开启时,对一批查询中的每一个有数据对象返回的查询都有单独一行的输出(不进行数据访问的查询语句不会产生任何输出,比如PRINT、SELECT变量的值或调用一个系统方法)。当IO统计选项启用时,会输出逻辑读的次数、物理读的次数、预读的次数和扫描计数。
逻辑读
这个数值指查询所需反问页的次数。对于任何给定的读取操作,数据缓存中的每一页都会被读取,但并不是所有的页都是必须的,因为数据缓存中的页来至磁盘。此值总是至少和物理读的值一样大,但通常比物理读的值大。同一页可以被读取多次(例如当一个查询使用了索引时),所以对某一个表逻辑都的次数可以比该表的页数大。
物理读
这个数值指从磁盘读取的页数,该值总是小于或等于逻辑读的值。由系统监测器显示的缓存命中率就是由逻辑读和物理读的次数计算得出的,公式如下:
缓存命中率 = (逻辑读 - 物理读) / 逻辑读
不要忘记物理读的次数可以差别很大,并且第二次及后续执行时物理读的次数会大幅减少,因为缓存在第一次执行时就完成了加载,物理读的次数就会比较小。出于这个原因,你可能认为没有必要为基于预查询的物理读做大量的分析。当查看单个的查询时,通常逻辑读的次数更令人关注,因为信息是一致的。物理I/O和缓存命中率很重要,但他们在服务器的层级更值得关注。
IO统计作用于每一个表和每一个查询。可能需要审查某些列,用sys.dm_exec_query_stats DMV追踪其物理读的次数。得到的信息包括最小物理读、最大物理读和物理读总次数,可以为每一个查询计划累计,还可以用sys.dm_exec_sessions获得每个会话的读取信息。
预读
预读数指在处理某个查询时,运用预读机制读到缓存中的页数。这些预读的页不一定被查询用到。如果用到了,只增加逻辑读的次数而不增加物理读的次数。一个较高的预读值意味着不进行预读处理时相比,物理读次数可能会更低,而缓存命中率可能会更高。在此情形下,不能由高缓存命中率断定系统不会从追加的内存获益。高命中率可能是由于预读机制读了很多查询所需的数据到缓存中。这是件好事,但是在缓存中简单地保留之前用过的数据可能会更好,这样,可能获得同样高甚至更高的命中率而不是用预读机制。
预读是物理IO的理想状态。在全表扫描或部分表扫描中,通过表的索引分配图来确定对象的分区。分区按块读取,单块64KB。索引分配图的组织方式决定了分区按磁盘序读取。如果表遍布文件组的多个文件,预读机制不再按顺序处理文件,而是尝试保持至少8个文件出于忙态。预读由执行查询的线程进行异步请求。正因为是异步的,所以扫描时不会因预读而被阻塞。只有要扫描的页恰巧是预读进缓存的页时才会阻塞。这样,预读既不至于比扫描太超前,也不至于太落后。
扫描计数
扫描计数显示了表被访问的次数。嵌套循环连接的外表扫描计数值为1,内表的扫描计数值可能为循环的次数,即表被访问的次数。逻辑读的次数取决于每次扫描访问的页次数的综合。然而,即便是嵌套循环连接,内表的扫描计数也有可能是1。SQL Server可能会在内表拷贝需要的行到缓存的工作表,使用工作表来访问实际的数据行。通常IO统计的输出不会显示计划执行了这一步骤。需要使用TIME统计的结果及实际处理计划所用时间的信息来确定执行查询所涉及的实际工作。哈希连接和合并连接的扫描计数值通常为1,因为两个表都涉及连接,但这类连接涉及更多内存。在执行查询时,可以使用sys.dm_exec_requests观察granted_query_memory的值,但请记住这不是一个累计计数器,只对当次执行的查询有效。相应地,用可以用sys.dm_exec_sessions监察memory_usage字段,以便观察每个会话使用了多个个8K内存页。