查询执行的总图:

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

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. Java多线程3:Thread中的静态方法

    Thread类中的静态方法 Thread类中的静态方法表示操作的线程是"正在执行静态方法所在的代码块的线程".为什么Thread类中要有静态方法,这样就能对CPU当前正在运行的线程 ...

  2. Java多线程2:Thread中的实例方法

    Thread类中的方法调用方式: 学习Thread类中的方法是学习多线程的第一步.在学习多线程之前特别提出一点,调用Thread中的方法的时候,在线程类中,有两种方式,一定要理解这两种方式的区别: 1 ...

  3. jenkins + Git 搭建持续集成环境

    持续集成通过自动化构建.自动化测试以及自动化部署加上较高的集成频率保证了开发系统中的问题能迅速被发现和修复,降低了集成失败的风险,使得系统在开发中始终保持在一个稳定健康的集成状态.jenkins是目前 ...

  4. iphone按home键后,正在运行的程序是否退出了呢?

    是否一直有个疑问,当iphone手机正在运行一个APP,如果按Home键后,那么原来正在运行的程序还在运行吗?如果开发过ios程序,可能不是问题,如果没有开发过的,可能会疑惑了,我就简单的说一下.以几 ...

  5. 如何在 ASP.NET MVC 中集成 AngularJS(1)

    介绍 当涉及到计算机软件的开发时,我想运用所有的最新技术.例如,前端使用最新的 JavaScript 技术,服务器端使用最新的基于 REST 的 Web API 服务.另外,还有最新的数据库技术.最新 ...

  6. Centos Another app is currently holding the yum lock

    yum命令用ctrl+z命令中断后,再运行yum时,出现: Existing lock /var/run/. Another app is currently holding the yum lock ...

  7. 黑马程序员_java08_多线程

    转载于:http://www.itxuexiwang.com/plus/view.php?aid=148 线程是程序中可以并行执行的任务. java运行系统总是选当前优先级最高的处于就绪状态的线程来执 ...

  8. 将不确定变为确定~DateTime.MinValue和MaxValue引发的异常

    回到目录 问题描述: SqlDateTime 溢出.必须介于 1/1/1753 12:00:00 AM 和 12/31/9999 11:59:59 PM 之间 概念相关 .Net中的DateTime结 ...

  9. 爱上MVC~为Html.EditorForModel自定义模版

    回到目录 挺有意思的一件事 对于MVC视图渲染来说,大家应该不会陌生,但对于模型的渲染,不知道是否听说过,主要是说Model通过它属性的相关特性(DataType,UIHint)来将它们自动渲染到Vi ...

  10. ThinkPHP框架里隐藏index.php

    本文所写的配置在ThinkPHP3.2.2上测试过.按理也兼容其它版本. 首先修改配置文件: 'URL_CASE_INSENSITIVE' => true, // 默认false 表示URL区分 ...