SQL执行计划解析(1) 执行计划基础

摘要:
SQL执行计划解析-执行计划基础(上)电子书链接地址执行计划,简单的说就是查询优化器计算的结果,表示执行被提交查询的效率最高的方式。执行计划告诉你查询是如何被执行的,因此它是DBA诊断低性能查询的基础手段。

SQL执行计划解析(1)- 执行计划基础(上)

电子书链接地址

执行计划,简单的说就是查询优化器计算的结果,表示执行被提交查询的效率最高的方式。执行计划告诉你查询是如何被执行的,因此它是DBA诊断低性能查询的基础手段。

1.查询提交后发生了什么

当查询被提交到Sql Server DataBase之后,很多过程就开始工作,最终目的是将数据尽快返回给用户或者存储起来,同时保持数据的一致性。这些过程为每个提交到服务器的查询服 务,因而有很多的不同的动作同时发生在服务器上,我们只关注T-SQL相关的事情,粗略的分为两个阶段:

  •   关系引擎(relational engine)中发生的过程
  •   存储引擎(storage engine)中发生的过程

在关系引擎中,查询优化器解析和处理查询,产生执行计划,然后执行计划被送到存储引擎(二进制方式),存储引擎使用执行计划来获取或更新数据。锁定、索引维护、事物等都发生在存储引擎里头。由于执行计划是由关系引擎产生的,我们将主要关注关系引擎。

1.1查询解析

T-sql查询到达服务器后去的第一个地方就是关系引擎,它被传递给一个进程来检查拼写和形式,这个处理进程就是查询解析过程。解析过程的输出是一个解析树。解析树代表了执行查询的逻辑步骤。如果T-sql语句不是一个数据操纵语言(DML)声明就不会被优化,举个例子,对于创建table,系统只有一种“正确”的方式,那么就没有机会 来提升性能。如果T-sql是DML声明,解析树就被传递给一个叫algebrizer的进程,algebrizer解析查询引用到的所有的对象、表、列 的名字,并且识别列的类型(varchar(50) vs nvarchar(25)等),除此之外还要执行一个叫做聚合绑定 的过程来决定聚合的位置。algebrizer进程很重要,因为查询里可能包或了别名、同义词或者不存在的名字,这些需要被解析,或者查询引用了不存在的对象。algebrizer的输出是query processsor tree,二进制形式,然后被传递给了查询优化器。

1.2查询优化器

查询优化器决定了数据能否访问索引、使用哪种连接还有其他很多东西。这种决定是基于开销的,所需的cpu、io等。查询优化器将会产生并评估很多的 计划(除非cache里已经有了),一般来说,选择开销最低的那个,比如运行最快,使用最少的资源、cpu、I/O的那个。执行速度仍然是最重要的因素, 如果能够更快返回结果,优化器会选择cpu密集型的过程。有时候优化器也会选择效率较低的计划,如果它认为花时间去评估很多的执行计划还不如采用较低效率 的过程。如果你提交了一个非常简单的查询,比方说,单表查询、没有索引、没有聚合、没有计算,那么优化器就不会花时间来计算优化,而是简单的使用trival plan

如果查询是非Trival的,那么优化器就会计算开销然后选择一个计划。因此它需要依赖sql server服务器维护的统计数据。统计数据是数据库收集的关于列和索引的数据,它描述了数据的分布(distribution)、唯一性(uniqueness)和选择性 (selectivity)。构成统计数据的信息使用一个直方图(histogram)和表格(tabulation)来表示,它是从200个平均分布的 数据点(data Points)取出来的表示特定数据的出现次数。这种“关于数据的数据”给优化器提供了计算所需的必要信息。

如果列和索引相关的统计数据存在,那么优化器就会使用它们来计算。缺省地,系统会为所有索引和那些用作谓词(predicate)、where子句的一部 分、join on子句的一部分的列创建和更新统计数据。Table变量不会产生统计数据,优化器始终假定它只有一行而无视它真正的大小。临时表有统计数据,和永久表的 统计数据存储在同一个直方图里供优化器使用。

优化器使用这些统计数据和query processor tree一起决定最佳的执行计划。这就意味着,它需要测试一系列的计划,测试不同的join类型,组织join的顺序,尝试不同的索引等等,直到达成它认 为的最快的执行计划。在这个计算中,每一步都赋予了一个数值,代表了优化器预估的时间开销(estimated cost),每一步的开销加起来就是执行计划的开销。

有必要指出,预估的开销毕竟是预估的,如果有无限的时间和完整的最新的统计数据,优化器就能找到执行查询的完美计划,但是优化器是试图在最短的时间 找到最佳的执行计划,并且明显的,可用的统计数据的质量也是有限的,因此,虽然这个开销估算是个非常有用的手段,但是不能精确的反映现实。

优化器决定执行计划后,实际的执行计划就被创建并且存储进内存空间plan cache,除非相同的执行计划cache里已经存在。优化器产生可能的执行计划(potential plans),和cache里边已经存在的进行比较,如果匹配就是用cache里边的那个。

1.3查询执行

执行计划产生后,操作就转移到了存储引擎,在这里根据执行计划实际执行查询。这里不再详细讨论,除了一点,千辛万苦生成的执行计划和设计执行的可能并不一样,比方说一下情景:

  • 执行计划超出了并行执行(parallel execution)的界限

*parallel execution 利用多处理器提高执行效率

  • 统计数据过期或者发生了改变

1.4预估的和实际的执行计划

如前所述,有两种不同的执行计划,第一个是由优化器产生的预估的执行计划(Estimated execution plan),操作符和步骤被贴了Logical标签,代表了优化器的观点,另一个是实际的执行计划(Actual execution plan),代表了实际发生的事情。

1.5重用执行计划

服务器产生执行计划开销是昂贵的,可能的情况下Sql Server会尽量保持和重用执行计划。执行计划生成后就被存储进内存Plan Cache。

执行计划并不是永久驻留内存,它们会慢慢地根据age变化从系统消失,age的计算公式为执行计划的预估开销*被使用的次数,例如一个计划它的开销 是10,被引用了5次,那么它的age值就是50。延迟写入(lazywriter)进程负责释放所有类型的cache(包括plan cache),它周期性地扫描cache里的对象,并每次减去一定的age值。如果达到下列条件,执行计划将会从内从中被清除:

1、系统需要更多内存

2、age值达到了0

3、执行计划没有被任何连接(connection)所引用

执行计划也不是不可改变的,有些事件或动作会迫使执行计划重新编译。记住这些很重要,因为重新编译执行计划的开销可能非常大,下面的动作会导致执行计划重新编译:

1、改变查询中引用的表的结构或schema

2、改变了查询中用到的索引

3、删除了查询中用到的索引

4、更新了查询用到的统计数据

5、调用了函数sp_recompile

6、对查询用到的表的keys进行了大量insert或delete操作

7、对带有触发器的表,因inserted和deleted导致的明显增长

8、一个查询中混合了ddl和dml

9、查询执行中改变了SET选项

10、改变了查询使用的临时表的结构或schema

11、改变了查询中用到的动态试图(dynamic views)

12、改变了查询中的游标选项

13、改变了远程行集,就像在分布式分割试图(distributed partitioned view)里边

14、使用客户端游标时,改变了FOR BROWSE选项

1.6为何预估和实际的执行计划可能不同

一般情况下,你看到的预估执行计划和实际执行计划很可能是一样的,然而当环境改变时可能会导致二者的不同。

  • 陈旧的统计数据
    统计数据和实际数据间的差异是导致两个执行计划不同的主要原因。通常发生在有数据插入和删除,改变了索引的键值以及分布。
    为了降低操作成本,原子性的统计数据操作是取样于数据的子集。这就意味着,随着时间推移,统计数据就越来越不能准确反映实际数据。
    这不仅会导致两个执行计划间的差异,还会导致产生“坏”的执行计划。
  • 非法的预估执行计划
    某些情况下,预估的执行计划根本无法工作,比如下边的例子:
    复制代码
    CREATE TABLE TempTable
    (
    Id
    INT IDENTITY(1, 1)
    ,Dsc
    NVARCHAR(50)
    );
    INSERT INTO TempTable ( Dsc )
    SELECT [Name]
    FROM [Sales].[Store];
    SELECT *
    FROM TempTable;
    DROP TABLE TempTable;
    复制代码

    你会得到一个错误

    Msg 208, Level 16, State 1, Line 7
    Invalid object name
    'TempTable'.

    优化器用于产生预估的执行计划,并不执行那个T-Sql。当通过algebrizer来运行声明的时候,由于查询并没有被执行,临时表并不存在,这就会导致错误。

  • 请求并行计算
    计划遇到并行计算的瓶颈时,会创建两个计划,实际执行哪个取决于查询引擎。所以你可能在预估执行计划里看到有(或没有)并行操作符。当计划实际被执行时,
    查询引擎决定了要么它不支持并行计算或者调用并行查询后,你可能看到一个完全不同的计划。

1.7执行计划的格式

1、图形方式

2、文本方式

SHOWPLAN_ALL

SHOWPLAN_TEXT

STATISTICS PROFILE

3、Xml方式

SHOWPLAN_XML

STATISTICS_XML

SQL执行计划解析(1)- 执行计划基础(下)

Getting Started

如果你不是sysadmin、dbcreator、db_owner,那么你需要权限查看执行计划

GRANT SHOWPLAN TO [username]

第一个例子非常简单,打开management studio,键入以下查询

SELECT *
FROM [dbo].[DatabaseLog];

点击动作条上Display estimated Execution Plan按钮,就看到了我们的第一个预估执行计划

SQL执行计划解析(1) 执行计划基础第3张

图1-1

我们稍后再加以解释,现在我们再抓一个实际的执行计划,和预估的执行计划不同,实际的执行计划并不代表优化器的计算结果,而是代表了查询执行时实际发生的事情,通常两者是一样的,但是有时也不一样,这取决于存储引擎对执行计划的修改。点击Include Actual Execution Plan按钮使之处于激活状态,点击Execute。

SQL执行计划解析(1) 执行计划基础第4张

我们这个例子里预估的执行计划和实际的执行计划是一致的。 左边的SELECT图标你将来会看到很多,通常可以忽略掉,它表示来自关系引擎的最终结果和格式(formatting)。
右边的Table Scan图标是第一个也是最容易查找的图标。 通常我们会从右到坐、从上到下地读一个图形执行计划。图标之间的意思是代表运算符(operator)之间的数据传递。
这个例子里边,我们只有一个Table Scan运算符产生了结果集。箭头的宽度代表了数据量的大小,箭头越宽意味这更多行的数据。这种可视的方式可能会给你错误的印象,你可以将鼠标悬停于箭头之上来查看它所代表的数据的具体行数。 每个图标下边都有个百分数,代表了该运算符相对于整个查询的开销,注意,这是个比率不是实际的数字,即使是0%也会有微小的开销。图标上边有个百分数表示查询在批量查询中的开销百分比,可以使用批量查询执行多个查询并获取执行计划。

ToolTips

每个图标和箭头都有一个与之关联的弹出窗口即ToolTips,鼠标悬停于其上就可以看到。预估执行计划中鼠标悬停于SELECT图标之上就会看到下图1-3.

SQL执行计划解析(1) 执行计划基础第5张

如图,优化器产生了以下几个数字

  1. Cached plan size -- 查询产生的执行计划将会占用stored procedure cache的多少内存,当你调查cache性能的时候这是个很有用的数字,用它能够查看哪些执行计划占用了更多的内存。
  2. Estimated Operator Cost --我们在图1-1中已经看到了这个开销百分比。
  3. Estimated Subtree Cost -- 它代表了前面的步骤到此步骤累计的开销,记得从右到坐读。这个数字对真实世界没有意义,它只是查询优化器用来决定运算符开销的算术评估值,它代表了优化器认为的运算符需要花费多少时间。
  4. Estimated Number of Rows -- 基于统计数据计算而出
  5. 上述信息下边还有sql声明

Table Scan的ToolTips如下图1-4

SQL执行计划解析(1) 执行计划基础第6张

图1-4

Logical Operation 代表了优化器的计算结果,即优化器认为的查询执行时应该发生什么操作,而Physical Operation代表了实际发生了什么操作,一般来说两者是一致的,但也有例外,第二章有更多讨论.
然后是I/O、CPU、Operator、Subtree的预估开销,Subtree就是执行树中到当前为止的部分。所有的预估都是基于列和索引的统计数据。I/O Cost 和CPU cost不是实际的运算符,而是查询优化器在计算中分配的开销数字。这些数字有助于确定开销是I/o密集型还是cpu密集型。你可能注意到Operator Cost 和Subtree Cost是一样的,这是因为Table Scan是我们唯一的运算符。Ordered布尔值表示数据是否处于排序状态。NodeID 节点序数,顺序是从左到右的,尽管阅读的最好方式是从右到坐。

运算符属性

右键点击图标,选properties,可以看到更多信息,多数我们已经遇到了,不过还有些新的。

SQL执行计划解析(1) 执行计划基础第7张

图1-5

Defined Values显示了该操作向过程追加的信息,这些信息可能是基本查询的一部分,我们这个例子里是被选中的列,也可能是查询过程中内部创建的值,比方说一些确定内部引用完整性的标记,也可能是聚合函数计数的占位符。Estimated Rebinds和Rewinds这两个值是执行计划里init()被调用的次数。Foreced Index,使用query hint强制使用某个索引的时候为True。Sql Server通过query hint提供了一些功能,使用户可以对如何执行查询进行某些控制。NoExpandHint 此项的概念和Forced Index基本相同,只不过是用于索引视图。

文本执行计划和Xml执行计划

要想获取文本执行计划,在查询开始加上一个命令就可以了

SET SHOWPLAN_ALL ON;

关闭文本计划,使用

SET SHOWPLAN_ALL OFF;

获取实际的文本计划,使用

SET STATISTICS PROFILE ON

SET STATISTICS PROFILE OFF

获取实际的和预估的xml执行计划,使用

SET SHOWPLAN_XML ON

SET SHOWPLAN_XML OFF

ps:关于文本和Xml执行计划的更多细节就略过了

使用Sql Server Profiler自动捕获执行计划

在开发阶段,我们可以通过上述几种方式来捕获执行计划。但是在测试服务器或者运行中的服务器,可能并不允许我们这样做,Sql Server2005中可以使用Profiler在运行查询的同时捕获Xml执行计划。
Profiler是一个强大的工具,使用户可以捕获关于事件的数据。Profiler的事件可以通过GUI界面进行手动跟踪,也可以自己定义跟踪,并在特定的时间自动运行。这些跟踪信息可以通过屏幕查看,也可以发送到文件或数据库的表中。

执行计划事件

  • Showplan Text : 此事件在每次查询时均触发,生成的计划类型和Showplan_Text声明相同,是Showplan Xml生成的内容的子集。
  • Showplan Text (unencoded) : 同上,不过此事件是以字符串形式展示信息而不是二进制方式。
  • Showplan All : 此事件在每次查询时均触发,生成的计划类型和Showplan_all声明相同.
  • Showplan All for Query Compile : 此事件生成的数据和Showplan All相同,不过此事件仅在查询编译时触发。
  • Showplan Statistics Profile:此事件生成实际的执行计划,方式和T-Sql命令Statistics Profile相同。它生成的信息是Statistics Xml命令或Showplan Xml Statistics Profile事件的子集。
  • Showplan Xml:此事件在每次查询时均触发,产生一个预估的执行计划,同Showplan_Xml.
  • Showplan Xml for Query Compile: 同上,仅在查询编译时触发。
  • Performance Statistics:和Showplan Xml for Query Compile 类似,此事件不仅捕获执行计划也捕获性能测量数据。此事件仅捕获其中定义的子事件类的Xml输出。此事件在第一次进入Cache时、编译时、重编译时以及从Cache里移除时触发。
  • Showplan Xml Statistics Profile:此事件在每个查询运行时产生其实际的执行计划。

使用Showplan xml或者Showplan Xml Statistics Profile捕获所有的执行计划毫无疑问会增加服务器的负担,这些都不是轻量级的事件。即使使用不常用的Showplan Xml for Query compile也会导致小的性能下降。谨慎使用。

捕获Showplan Xml跟踪

启动Profiler -> 新建一个跟踪并连接到服务器 –> 切换至Events Selection标签页 –> 勾选Show all events复选框 –> 点击+号展开Performance部 –> 勾选Showplan xml事件

现在就可以在Profiler里捕获Showplan xml事件了,通常同时捕获一些其他的基本事件会有用处,如:RPC:Completed, SQL:BatchStarting,SQL:BatchCompleted等。

1-6

图1-6

当勾选了Showplan Xml事件之后,第三个标签页就出现了,在那里可以指定将输出的Xml发送往独立的文件。

点击Run启动跟踪,当有查询执行时就可以获得类似下图的结果

1-7

图1-7

TextData列里就是实际的Xml执行计划。选中的时候下边的窗口就出现了图形执行计划。你可以右键点击然后选择Extract Event data来将计划存到另外的文件里。

免责声明:文章转载自《SQL执行计划解析(1) 执行计划基础》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇图算法概论Spring Cloud 2020.0.5 发布,新特性一览,别掉队了!下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

Mysql数据优化--DBA梳理珍藏篇

1. 优化SQL1)     通过show status了解各种sql的执行频率         show status like 'Com_%'        了解 Com_select,Com_insert 的执行次数 2)    通过Explain分析低效的sql语句 3)    建立合适的索引 4)    通过show status like '...

数据库之索引与慢查询优化

一、介绍 1.什么是索引? 一般的应用系统,读写比例在10:1左右,而且插入操作和一般的更新操作很少出现性能问题,在生产环境中,我们遇到最多的,也是最容易出问题的,还是一些复杂的查询操作,因此对查询语句的优化显然是重中之重。说起加速查询,就不得不提到索引了。 2.为什么要有索引呢? 索引在MySQL中也叫做“键”,是存储引擎用于快速找到记录的一种数据结构。...

mysql_【MySQL】常见的mysql 进程state

Analyzing 线程是对MyISAM 表的统计信息做分析(例如, ANALYZE TABLE )。 checking permissions 线程是检查服务器是否具有所需的权限来执行该语句。 Checking table 线程正在执行表检查操作。 cleaning up 线程处理一个命令,并正准备以释放内存和重置某些状态变量。 closing tabl...

【转】Python 数据库连接池

    python编程中可以使用pymysql进行数据库连接及增删改查操作,但每次连接mysql请求时,都是独立的去请求访问,比较浪费资源,而且访问数量达到一定数量时,对mysql的性能会产生较大的影响。因此实际使用中,通常会使用数据库的连接池技术,来访问数据库达到资源复用。 python的数据库连接池包:DBUtils DBUtils提供两种外部接...

疑难杂症--SQL SERVER 18056的错误

朋友遇到一个很棘手的问题,查看服务器日志,报以下错误: 01/21/2014 11:47:43,spid296,未知,错误: 18056,严重性: 20,状态: 29。 01/21/2014 11:47:43,spid495,未知, The client was unable to reuse a session with SPID 495<c/...

jsp 部署

  部署一个Servlet 3.0应用程序是一件轻而易举的 事。通过Servlet注解类型,对于不太复杂的应用程序, 可以部署没有描述符的Servlet/JSP应用程序。尽管如 此,在需要更加精细配置的情况下,部署描述符仍然需 要。首先,部署描述符必须被命名为web.xml并且位于 WEB-INF目录下,Java类必须放置在WEB-INF/classes...