查询执行的总图:

根据总图的流程,详细说明每个部分:

1. 请求(Request)

SQL Server是C/S架构的平台。与它交互的唯一方式就是发送包含数据库命令的请求。应用程序和数据库之前的通信协议叫做TDS(Tabular Data Stream)协议。应用程序可以使用以下几种实现了TDS协议的客户端:

  • The CLR managed SqlClient,
  • OleDB,
  • ODBC,
  • JDBC,
  • PHP Driver for SQL Server
  • 开源的FreeTDS

TDS的请求分为以下几类:

  • 批请求(Batch Request)

这种请求只包括T-SQL文本,不包含参数,但是可以包含本地变量。在SqlClient中带有空参数列表的SqlCommand对象上执行SqlCommand.ExecuteReader(), ExecuteNonQuery(), ExecuteScalar()或者ExecuteXmlReader(),就会是批请求。通过Profile会观察到SQL:BatchStarting事件。

它包含过程标识符(Procedure Identifier,用于)和任意数理的参数。不同的过程标识符代表了不同的系统存储过程。执行带有非空参数列表的SqlCommand对象时,就是这种请求类型。通过Profile可以观察到RPC:Starting事件。

  • 批量加载请求(Bulk Load Request)

批量加载是批量插入操作所使用的一种特别的请求类型。例如BCP工具、OleDB的IRowsetFastLoad接口和SqlBulkcopy类。它是唯一一种在TDS协议中不需要完成包发送就可以开始执行的请求。开始执行后,就可以使用数据流中的数据进行插入操作了。

2. 任务(Task)

当完整的请求到达数据库引擎,SQL Server会创建一个Task去处理此请求。可以通过sys.dm_exec_requests观察请求情况。一个任务代表一个完整的请求,而不会是请求的一部分语句。同样,对于请求中的部分语句,也不会创建新的任务。有些请求的中语句会并行执行,而Task会生成sub-task处理并行。当客户端取走所有请求返回的结果集中的数据后,Task就完成了。

可以通过sys.dm_os_tasks观察Task情况。

3. 工作进程(Workers)

根据新请求所创建的Task,初始状态是PENDING。这个阶段,SQL Server并不知道请求的内容。Task须要执行这个请求,引擎就会分配worker去执行。(是分配,不是创建)

Workers是SQL Server的线程池。在SQL Server启动过程中会初始化一定数量的Workers。可以通过Max_Worker_Threads参数,按需要配置最大线程数。只有Worker才执行代码。当没有空闲的worker时,task变成Pending状态。worker完成task后,变成可用状态,才会去选择Pending状态的task执行。

在SQL批请求中,worker选择task后,执行批请求中的每一个语句。很明显,批请求中语句,是串行执行的。前一个完成,才会开始下一个。批中的某些语句会并行执行,这个并行是Task创建sub-task来完成的。而每一个sub-task会经历和task一样处理过程(如等待可用的worker来选取它并执行)。

可以通过sys.dm_os_workers查看worker及其状态。

4. 语法解析和编译(Parsing &  Compilation)

当task开始执行,首先它要弄明白请求的具体的内容。这个阶段SQL Server对请求中的T-SQL文本进行语法解析并生成表示请求的抽象语法树(abstract syntax tree)。整个请求会被解析和编译。如果在这个阶段产生错误,则会返回编译错误,并结果任务并释放task和worker。

编译T-SQL不会产生像本地CPU指令一样的可执行代码,也产生类似于字节码的东西。它产生查询计划(Query Plan)。查询计划描述了数据访问的路径和访问对象的方法。

5. 优化(Optimization)

优化是从很多个查询计划中选择出最优的一个。SQL Server采用基于成本的优化器。它会估算所有可能(大多数)的查询计划的成本,并选择出成本最低的一个。成本主要通过计算查询计划需要读取的数据大小(data size)。为了知道数据大小,SQL Server需要知道每个表的大小和列值的分布情况(通过统计信息数据)。成本还会考虑CPU和内存使用量。再通过一个公式将这些数据综合个成本值,然后选取出成本值最小的那个执行计划。

优化过程需要消耗时间和CPU,所以一当查询计划最终生成,则会被缓存到计划缓存中,以备重用。

6. 执行(Execution)

一旦优化器选定了执行计划,请求就可以开始执行了。执行计划会被转换成实际的执行树。树中的每个节点是一个操作符。所有操作符都实现三个抽象接口:open()、next()和close()。循环执行包括调用根节点的open(),然后逐级调用next()直到返回false,再调用close()。

叶级节点通常是一些物理数据访问操作符(访问实际的数据和索引),中间节点通常是一些实现数据过滤、排序和连接等数据操作的操作符。并行执行有一个专门的操作符:Exchange操作符。Exchange操作符发出多个线程,每个线程执行一个查询计划的子树,然后再使用multiple-producers-one-consumer 方式聚合所有子线程的输出。

数据修改操作也适用于这个执行方式。

有些操作符非常简单,如TOP(N)。当调用它的next(),它会去调用子节点的next()并记录数据。当重复执行N次后,它就返回false,并终止对子节点的调用和对相应分支子树的迭代执行。

有些操作符非常复杂,如nested loop操作符。这需要跟踪内外子节点循环迭代的位置,调用外节点的next(),值重绕(rewind)内节点并不断调用内节点的next()直到找到匹配的值。

有些操作符需要等到获取到它的所有子节点的输出数据时,才能产生自己的输出数据。这种行为方式也叫stop-and-go。如sort操作符,它第一次调用netxt(),不会返回数据,需要等到所有的数据被返回并排序,这才能返回数据。

HASH JOIN是一个非常复杂并且又是stop-and-go类型的操作符。为了构造hash表,它要调用构建侧(build side)节点的next(),直到返回false。然后再调用探测侧(probe side)的next(),直到找到在hash表中找到匹配的值,然后返回。重复探测侧的操作,直到next()返回false。

7. 返回结果(Results)

查询一旦开始执行就可以开始返回数据给客户端程序。当执行树开始产生返回数据后,最顶端的操作符会负责把数据写入网络缓存并发送给客户端。执行中产生的返回结果,不会被缓存到任何地方,一但产生就开始返回给客户端。

显然,通过网络返回数据给客户端会受到网络流量控制协议的约束。如果客户端不能及时地取走返回的数据,最终会阻塞数据发送方的发送行为,并使得查询执行被挂起。当客户端的数据接收能力正常后,发送方的发送行为和查询执行会被重置,正常产生返回结果数据。

OUTPUT参数的输出值,只能在执行计划完成后,才能被写入到数据流中。所以它也只能在所有返回结果被客户端取走后,才能被读取到。

总结:

1. 这是一篇译文,计划分为3部分。学习之用,非逐字翻译,很多是结合自己的理解译的,与原文内容相比,有一些增和删。

2. 原文地址:Understanding how SQL Server executes a query

理解SQL Server是如何执行查询的 (1/3)的更多相关文章

  1. 理解SQL Server是如何执行查询的 (2/3)

    查询执行的内存授予(Query Execution Memory Grant) 有些操作符需要较多的内存才能完成操作.例如,SORT.HASH.HAS聚合等.执行计划通过操作符需要处理数据量的预估值( ...

  2. 理解SQL Server是如何执行查询的 (3/3)

    页并发访问的保护:闩锁 在多线程并发情况下,需要防止读线程读到写线程正在写的资源,在编程中,通过使用互斥器(Mutexes), 信号量(Semaphore), 临界区(Critical Section ...

  3. 理解SQL Server是如何执行查询的---Joe-T :mvp

    http://www.cnblogs.com/Joe-T/ http://rusanu.com/2013/08/01/understanding-how-sql-server-executes-a-q ...

  4. 理解SQL Server中的权限体系(下)----安全对象和权限

    原文:http://www.cnblogs.com/CareySon/archive/2012/04/12/SQL-Security-SecurableAndPermission.html 在开始阅读 ...

  5. 理解SQL Server中的权限体系(上)----主体

    原文:http://www.cnblogs.com/CareySon/archive/2012/04/10/mssql-security-principal.html 简介 权限两个字,一个权力,一个 ...

  6. (转)理解SQL SERVER中的分区表

    简介 分区表是在SQL SERVER2005之后的版本引入的特性.这个特性允许把逻辑上的一个表在物理上分为很多部分.而对于SQL SERVER2005之前版本,所谓的分区表仅仅是分布式视图,也就是多个 ...

  7. 理解SQL Server中索引的概念

    T-SQL查询进阶--理解SQL Server中索引的概念,原理以及其他   简介 在SQL Server中,索引是一种增强式的存在,这意味着,即使没有索引,SQL Server仍然可以实现应有的功能 ...

  8. 理解SQL SERVER中的分区表(转)

    简介 分区表是在SQL SERVER2005之后的版本引入的特性.这个特性允许把逻辑上的一个表在物理上分为很多部分.而对于SQL SERVER2005之前版本,所谓的分区表仅仅是分布式视图,也就是多个 ...

  9. 理解SQL SERVER中的分区表

    转自:http://www.cnblogs.com/sienpower/archive/2011/12/31/2308741.html 简介 分区表是在SQL SERVER2005之后的版本引入的特性 ...

随机推荐

  1. DWZ错误的解决:0x800a13af - Microsoft JScript 运行时错误: 重新声明常量“document”

    在写完Login后,需要跳转到Index中,就是DWZ的主界面,结果出现如下问题: 0x800a13af - Microsoft JScript 运行时错误: 重新声明常量“document” 费了很 ...

  2. Xamarin.Android经验之谈

    1.Fragment如何做到显示才加载数据 有些界面我们会采用套用多个Fragment来显示的效果,但是我们不会在一显示这个活动的时候就把所有的Fragment加载并加载数据,而是会让显示出来的Fra ...

  3. JQuery源码解析(一)

    写在前面:本<JQuery源码解析>系列是基于一些前辈们的文章进行进一步的分析.细化.修改而写出来的,在这边感谢那些慷慨提供科普文档的技术大拿们. 要查阅JQ的源文件请下载开发版的JQ.j ...

  4. ECMAScript 6 简介

    ECMAScript 6 是JavaScript的下一个标准,正处在快速开发之中,大部分已经完成了,预计将在2014年正式发布.Mozilla将在这个标准的基础上,推出JavaScript 2.0. ...

  5. 一道原生js题目引发的思考(鼠标停留区块计时)

    我瞎逛个啥论坛,发现了一个题目,于是本着练手的心态就开始写起来了,于是各种问题接踵而至,收获不小. 题目是这样的: Demo: mouseenter与mouseover区别demo 跨浏览器的区块计数 ...

  6. ASP.NET MVC实现仪表程序

    1.1.1 摘要 在大多数情况下,我们的Web程序不仅仅需要给用户提供具体数据,在一些情况下,我们还需要给高级的用户或管理者提供数据汇总和分析图表之类的功能. 如果我们不想显示一大堆烦心的数据,希望通 ...

  7. 说说设计模式~策略模式(Strategy)

    返回目录 策略模式定义了一系列的算法,并将每一个算法封装起来,而且使它们还可以相互替换.而对于客户端(UI)来说,可以通过IOC再配合工厂模块,实现动态策略的切换,策略模块通常于一个抽象策略对象(in ...

  8. salesforce 零基础学习(二十八)使用ajax方式实现联动

    之前的一篇介绍过关于salesforce手动配置关联关系实现PickList的联动效果,但是现实的开发中,很多数据不是定死的,应该通过ajax来动态获取,本篇讲述通过JavaScript Remoti ...

  9. salesforce 零基础开发入门学习(七)PickList的value值获取

    之前介绍过PickList类型的声明以及赋值,但是如何取出呢?一个sObject对象可以理解为一条数据.通过sObject直接取恐怕很难做到,因为他只会显示一个值.这时候就要用到Schema命名空间中 ...

  10. 将图片的二进制字节 在HTML页面中显示

    两种方法: 后端的一般处理程序:Imge.ashx using System; using System.Collections.Generic; using System.Linq; using S ...