SQL Server Query Execution Plan Analysis
SQL Server Query Execution Plan Analysis
Source:http://www.sql-server-performance.com/tips/query_execution_plan_analysis_p1.aspx
当需要分析某个查询的效能时,最好的方式之一查看这个查询的执行计划。执行计划描述SQL Server查询优化器如何实际运行(或者将会如何运行)一个特定的查询。
查看查询的执行计划有几种不同的方式。它们包括:
SQL Server查询分析器里有一个叫做”显示实际执行计划”的选项(位于”查询”下拉菜单中)。如果打开了这个选项,那么无论何时在查询分析器中运行一个查询,都会得到一个显示在单独窗口的查询执行计划(以图形的格式)。
如果只是想看下执行计划而不想运行查询,那么可以选择”显示预估的执行计划”选项(位于”查询”下拉菜单中)。当选择这个选项后,执行计划会马上显示出来(以图形的格式)。两者的不同之处在于当实际运行一个查询时,当前的服务器上的运算也会被考虑进去。大多数情况下,两种方式产生的执行计划产生的结果是相似的。
当建立一个SQL Server Profiler追踪时,可以收集的一个事件是MISC: Execution Plan.这个信息(以文本的形式)显示查询优化器用来执行查询的计行计划。
可以在查询分析器上执行SET SHOWPLAN_TEXT ON命令。这条命令被执行后,所有在当前这个查询分析器会话中执行的查询都不会运行,而是会显示一个基于文本的执行计划。执行某条用到临时表的查询时,必须在执行查询先运行SET STATISTICS PROFILE ON语句。
上面这些选项中,我更喜欢使用”显示实际执行计划”这个选项。它以图形的方式输出信息,并且考虑到了当前服务器上的那些运算。[7.0, 2000] Updated 8-5-2005
*****
如果在执行计划中看到如下所示的任何一项,就应该将它们视作警告信号并调查它们以找出潜在的性能问题。从性能方面来说,下面所示的每一项都是不理想的。
Index or table scans(索引或者表扫描):可能意味着需要更好的或者额外的索引。
Bookmark Lookups(书签查找):考虑修改当前的聚集索引,使用复盖索引,限制SELECT语句中的字段数量。
Filter(过滤):在WHERE从句中移除用到的任何函数,不要在SQL语句中包含视图,可能需要额外的索引。
Sort(排序):数据是否真的需要排序?可否使用索引来避免排序?在客户端排序是否会更加有效率?
无一例外地避免这些操作是不可能的,但是避免得越多,查询性能就会越快。[7.0,2000,2005]
*****
如果有在存储过程中或者其它T-SQL批处理代码中用到了临时表,就不能在查询分析器或Management Studio使用”显示预估的执行计划”选项来评估查询。必须实际运行这个存储过程或者批处理代码。这是因为使用”显示预估的执行计划”选项来运行一个查询时,它并没有实际被运行,临时表也没有创建。由于临时表没有被创建,参考到临时表的代码就会失败,导致预估的执行计划不能成创建成功。
从另一方面来说,如果使用的是表变量而不是临时表,则可以使用”显示预估的执行计划”选项。[7.0,2000,2005] Updated 8-5-2005
*****
如果在查询分析器或Management Studio中对一个非常复杂的查询的执行计划进行分析,可能会觉得它的执行计划既难于看懂也难于分析。那么,按照查询的逻辑将它拆分成几个部分,然后分别对这些部分进行分析会容易得多。[7.0,2000,2005] Updated 8-5-2005
*****
图形执行计划并不总是容易读懂和解释。查看执行计划时记住如下几点:
非常复杂的执行计划会被分成多个部分,它们分别列出在屏幕上。每个部分分别代表查询优化器为了得到最终结果而必须执行的单个处理或步骤。
执行计划的每个步骤经常会被拆分成一个个更小的子步骤。不幸的是,它们是从右至左显示在屏幕上的。这意味着你必须滚动到图形执行计划的最右边去查看每个步骤是从哪儿开始的。
每个步骤与子步骤间通过箭头连接,藉此显示查询执行的路径。
最后,查询的所有部分在屏幕顶部的左边汇总到一起。
如果将鼠标移动到任何执行计划任何步骤或者子步骤的上面,就会显示一个弹出式窗口,上面显示该步骤或子步骤的更加详细的信息。
如果将鼠标移动到连接步骤或子步骤的箭头上,就可以看到一个弹出式窗口,上面显示有多少笔记录从一个步骤或子步骤移动到另一个步骤或子步骤。
[7.0, 2000, 2005] Updated 8-5-2005
图形执行计划上连接每个图标的箭头粗细不同。箭头的粗细表示每个图标之间移动的数据行数量以及数据行大小移动所需的相对成本。箭头越粗,相对成本就越高。
可以使用这个指示器来快速测量一个查询。你可能会特别关注粗箭头以了解它如何影响到查询的效能。例如,粗线头应该在图形执行计划的右边,而非左边。如果看到它们在左边,就意味着太多的数据行被返回,这个执行计划也不是最佳的执行计划。[7.0,2000,2005]
*****
执行计划的每个部分都被分配了一个成本百分比。它表示这个部分耗用了整个执行计划的多少资源。当对一个执行计划进行分析的时候,应该将精力集中于有着高成本百分比的那些部分。这样就可以在有限的时间里找到可能性最大的问题,从而回报了你在时间上的投资。[7.0, 2000, 2005]
*****
你可能会注意到一个执行计划的某些部分被执行了不止一次。作为执行计划分析的一部分,应该将你的一些时间集中在任何执行了超过一次的那些部分上,看看是否有什么方式减少它们执行的次数。执行的次数越少,查询的速度就越快。[7.0, 2000, 2005]
*****
在执行计划中你可以看到I/O与CPU成本。它们没有”实际”的意义,例如代表特定资源的使用量。查询优化器使用这些数字来做出最佳选择。它们可用来参考的一个意义是,较小的I/O或CPU成本比较大的I/O或CPU成本使用更少的服务器资源。[7.0, 2000, 2005]
*****
查看SQL Server图形执行计划时,可以查找的非常有用的一个东西就是查询优化器如何为给定的查询使用索引来从表中获取数据。通过查看是否有用到索引,以及索引如何被使用,都有助于判断当前的索引是否使得查询执行得尽可能的快。
将鼠标移到图形执行计划上的表名(以及它的图标)上面,就会弹出一个窗口,从它上面可以看到一些信息。这些信息让你知道是否有用到索引来从表中获取数据,以及它是如何使用的。这些信息包括:
- Table Scan(表扫描):如果看到这个信息,就说明数据表上没有聚集索引,或者查询优化器没有使用索引来查找。意即资料表的每一行都被检查到。如果资料表相对较小的话,表扫描可以非常快速,有时甚至快过使用索引。
因此,当看到有执行表扫描时,第一件要做的事就是看看数据表有多少数据行。如果不是太多的话,那么表扫描可能提供了最好的总体效能。但如果数据表大的话,表扫描就极可能需要长时间来完成,查询效能就大受影响。在这种情况下,就需要仔细研究,为数据表增加一个适当的索引用于这个查询。
假设你发现某查询使用了表扫描,有一个合适的非聚集索引,但它没有用到。这意味着什么呢?为什么这个索引没有用到呢?如果需要获得的数据量相对数据表大小来说非常大,或者数据选择性不高(意味着同一个字段中重复的值很多),表扫描经常会比索引扫描快。例如,如果一个数据表有10000个数据行,查询返回1000行,如果这个表没有聚集索引的话,那么表扫描将比使用一个非聚集索引更快。或者如果数据表有10000个数据行,且同一个字段(WHERE条件句有用到这个字段)上有1000笔重复的数据,表扫描也会比使用非聚集索引更快。
查看图形执行计划上的数据表上的弹出式窗口时,请注意”预估的资料行数(Estimated Row Count)”。这个数字是查询优化器作出的多少个数据行会被返回的最佳推测。如果执行了表扫描且”预估的数据行数”数值很高的话,就意味着返回的记录数很多,查询优化器认为执行表扫描比使用可用的非聚集索引更快。
- Index Seek(索引查找):索引查找意味着查询优化器使用了数据表上的非聚集索引来查找数据。性能通常会很快,尤其是当只有少数的数据行被返回时。
- Clustered Index Seek(聚集索引查找):这指查询优化器使用了数据表上的聚集索引来查找数据,性能很快。实际上,这是SQL Server能做的最快的索引查找类型。
- Clustered Index Scan(聚集索引扫描):聚集索引扫描与表扫描相似,不同的是聚集索引扫描是在一个建有聚集索引的数据表上执行的。和一般的表扫描一样,聚集索引扫描可能表明存在效能问题。一般来说,有两种原因会引此聚集索引扫描的执行。第一个原因,相对于数据表上的整体数据行数目,可能需要获取太多的数据行。查看”预估的数据行数量(Estimated Row Count)”可以对此加以验证。第二个原因,可能是由于WHERE条件句中用到的字段选择性不高。在任何情况下,与标准的表扫描不同,聚集索引扫描并不会总是去查找数据表中的所有数据,所以聚集索引扫描一般都会比标准的表扫描要快。通常来说,要将聚集索引扫描改成聚集索引查找,你唯一能做的是重写查询语句,让语句限制性更多,从而返回更少的数据行。
[7.0, 2000, 2005]
绝大多数情况下,查询优化器会对连接进行分析,按最有效率的顺序,使用最有效率的连接类型来对数据表进行连接。但并不总是如此。在图形执行计划中你可以看到代表查询所使用到的各种不同连接类型的图标。此外,每个连接图标都有两个箭头指向它。指向连接图标的上面的箭头代表该连接的外部表,下面的箭头则代表这个连接的内部表。箭头的另一头则指向被连接的数据表名。
有时在多表连接的查询中,箭头的另一头指向的并不是一个数据表,而是另一个连接。如果将鼠标移到指向外部连接与内部连接的箭头上,就可以看到一个弹出式窗口,告诉你有多少数据行被发送至这个连接来进行处理。外部表应该总是比内部表含有更少的数据行。如果不是,则说明查询优化器所选择的连接顺序可能不正确(下面是关于这个话题的更多信息)。
首先,让我们来看看连接类型。SQL Server可以使用三种不同的技术来连接资料表:嵌套循环(nested loop),散列(hash),以及合并(merge)。一般来说,嵌套循环是最快的连接类型,但如果不可能使用嵌套循环的话,则会用到散列或者合并作为合适的连接类型。两者都比嵌套循环连接慢。
当连接大表时,则合并连接可能是最佳选项,而非嵌套循环连接。唯一的明确这一点的方式是对两者都进行测试以查看哪一个最有效率。
如果你怀疑某个查询速度慢的原因可能是因为它所使用的连接类型不理想,那么你可以使用连接提示来复盖查询优化器的选择。在使用连接提示之前,你需要花费一些时间去了解一下每种连接类型以及它们的工作方式。这是一个复杂的话题,超出了本文的讨论范围。
查询优化器选择最有效率的连接类型来连接数据表。例如,嵌套循环连接的外部表应该是连接的两个表中较小的那个表。散列连接也是一样,它的外部表应该是较小的那个表。如果你觉得查询优化器选择的连接顺序是错误的,可以使用连接提示来复盖它。
很多情况下,唯一的确认使用连接提示改变连接类型或连接顺序是提升还是降低了效能的方式,就是对它们进行测试,看看发生了什么。[7.0, 2000, 2005]
*****
如果你的SQL Server有多个CPU,并且没有修改SQL Server的默认设置来限制SQL Server使用服务器上所有CPU的能力,那么查询优化器会考虑使用平行处理(parallelism)来执行某些查询。平行处理指在多个CPU上同时运行一个查询的能力。很多情况下,一个运行在多个处理器上的查询比仅运行在单个处理器上的查询要快,但并不总是这样。
查询优化器并不会总是使用平行处理,即使在它能使用的时候。这是因为查询优化器在决定使用平行处理前会考虑到各种不同的因素。例如当前SQL Server上处于活动状态的连接数量,CPU忙碌程度,是否有足够的内存来运行平行化查询,需要处理的数据行数量,以及这个查询的类型。查询优化器收集到这些真实的数据后,再决定平行处理是不是运行这个查询的最佳选择。你可能会发现,某次一个查询没有用到平行处理,但稍后某次再次运行同样的查询时,却又用到了平行处理。
有时,使用多个处理器所需的花费会大于使用它们能所能节省的资源。尽管查询处理器的确会衡量使用平行查询的正反两面的影响,但它的猜想并不总是正确的。
如果怀疑平行处理防碍了某条查询的性能,你可以使用OPTION (MAXDOP 1)提示来关闭该查询的平行处理。
决定是否使用平行处理的唯一方式是通过这两种方式对查询进行测试,看看发生了什么。[7.0, 2000, 2005]
*****
查看图形执行计划时,你可能会发现某个图标的文字用红色显示,而非通常情况下的黑色。这意味着相关的表的一些统计数据遗失,统计数据是查询优化器生成一个好的执行计划所必须的。
遗失的统计数据可以通过右键这个图标,并选择”创建遗失的统计资料”来创建。这时会弹出”创建遗失的统计数据”对话框,通过它可以很容易地创建遗失的统计数据。
当可以选择去更新遗失的统计资料时,应该总是这样做,因为这样极有可能让你正在分析的查询语句从中获得效能上的好处。[7.0, 2000, 2005]
*****
有时你会在图形执行计划上看到标识了”Assert”的图标。这意味着查询优化器正在验证查询语句是否有违反引用完整性或者条件约束。如果没有,则没有问题。但如果有的话,查询优化器将无法为该查询建立执行计划,同时会产生一个错误。[7.0, 2000, 2005]
*****
你常常会在图形执行计划上看到标识成”书签查找(Bookmark Lookup)”的图标。书签查找相当常见。书签查找的本质是告诉你查询处理器必须从数据表或者聚集索引中来查找它所需要的数据行,而不是从非聚集索引中直接读取。
打比方说,如果一个查询语句的SELECT,JOIN以及WHERE子句中的所有字段,都不存在于那个用来定位符合查询条件的数据行的非聚集索引中,那么查询优化器就不得不做额外的工作在数据表或聚集索引中查找那些满足这个查询语句的字段。
另一种引起书签查找的原因是使用了SELECT *。由于在绝大多情况下它会返回比你实际所需更多的数据,所以应该永不使用SELECT *.
从性能方面来说,书签查找是不理想的。因为它会请求额外的I/O开销在字段中查找以返回所需的数据行。
如果认为书签查找防碍了查询的性能,那么有四种选择可以用来避免它:可以建立WHERE子句会用到的聚集索引,利用索引交集的优势,建立覆盖的非聚集索引,或者(如果是SQL Server 2000/2005企业版的话)可以建立索引视图。如果这些都不可能,或者使用它们中的任何一个都会耗用比书签查找更多的资源,那么书签查找就是最佳的选择了。[7.0, 2000, 2005]
有时查询优化器需要在tempdb数据库中建立临时工作表。如果是这样的话,就意味着图形执行计划中有标识成Index Spool, Row Count Spool或者Table Spool的图标。
任何时候,使用到工作表一般都会防碍到性能,因为需要额外的I/O开销来维护这个工作表。理想情况下应该不要用到工作表。不幸的是并不能总是避免用到工作表。有时当使用工作表比其它选择更有效率时,它的使用实际上会增强性能。
不论何种情况,图形执行计划中的工作表都应该引起你的警觉。应该仔细检查这样的查询语句,看看是否有办法重写查询来避免用到工作表。有可能没有办法。但如果有的话,你就朝提升这个查询的性能方面前进了一步。[7.0, 2000, 2005]。
*****
在图形执行计划上看到流聚合(Stream Aggregate)图标就意味着有对一个单一的输入进行了聚合。当使用了DISTINCT子句,或者任何聚合函数时,如AVG, COUNT, MAX, MIN,或者SUM等,流聚合操作就相当常见。 [7.0, 2000, 2005]
*****
查询分析器与Management Studio不是唯一的可以生成、显示查询执行计划的工具。SQL Server Profiler也可以显示执行计划,但格式是文本形式的。使用SQL Server Profiler来显示执行计划的一个优势是,它能为实际运行的大量查询产生执行计划。如果使用查询分析器和Management Studio,则一次只能运行一个。
使用Profiler捕获、显示执行计划时,必须使用如下的配置生成一个追踪:
捕获事件
- Performance: Execution Plan
- Performance: Show Plan All
- Performance: Show Plan Statistics
- Performance: Show Plan Text
显示的字段
- StartTime
- Duration
- TextData
- CPU
- Reads
- Writes
过滤条件
- Duration。你会想指定最大的查询执行时间,例如5秒钟,由此避免得到太大量的数据。
当然,你可以在你的追踪中捕获更多的没有例在上面的信息,上面例出的只是一个指南而已。但必须记住不要去捕获太多的数据,否则,追踪的运行会影响服务器的性能。[7.0, 2000, 2005]
*****
如果在查询中使用了OPTION FAST提示,那就必须小心执行计划的结果可能不是你所期望的。这时你所看到的执行计划基于使用了FAST提示的结果,而不是整个查询语句的实际执行计划。
FAST提示用来告知果询优化器尽可能快地返回指定行数的数据行,即便这样做会防碍查询的整体性能。使用这个提示的目的在于为使用者快速返回特定行数的记录,由此让他们产生速度非常快速的错觉。。当返回指定行数的数据行后,剩余的数据行按照它们通常的速度返回。
因此,如果使用了FAST提示,那么生成的执行计划只是基于那些FAST返回的数据行,而非查询要返回的所有数据行。如果想看所有数据行的执行计划,那么就必须移除这个FAST提示。[2000,2005]