设为首页
加入收藏夹

Microsoft SQL Server 查询处理器的内部
浏览选项:

  准备/执行模型
  除了执行直接模型(在 ODBC 中用 SQLExecDirect 调用)外,在 ODBC 和 OLE-DB 中,
还有一种执行模型,称为准备/执行模型。定义要执行的 SQL,是作为一个独立于实际执行
  SQLPrepare(hstmt, "SELECT * FROM parts where partid =
  ?", SQL_NTS)
  SQLExecute(hstmt)
  在 SQL Server 7.0 版本之前,准备/执行从来都不是 SQL Server 的本机模式。如今在
7.0 版本中,有两个提供本机接口的虚拟系统存储过程。对于准备调用,我们要再次研究游标
的类型,然后调用 sp_prepare 或 sp_cursorprepare。这些过程会完成 SQL 或存储过程的
编译,但不会实际执行计划。相反,虚拟系统存储过程只是返回该计划的句柄。现在,应用程
序可以反复地执行 SQL 了,例如传入不同的参数值,而不需要重新编译。
  在 SQL Server 6.5 中,由于没有本机接口,需要模拟准备和执行两个阶段。可以通过下
面的两种方法做到这一点。在第一种方法中,不会真正出现准备阶段。只有执行部分返回元数
据(有一些选项可以做到这一点),所以 SQL Server 可以把结果的格式描述返回给应用程序
。在第二种方法中,SQL Server 实际上创建一个特定存储过程,这个过程是单个用户私用的
,不能共享计划。这第二种方法可能会占满 tempdb 数据库的空间,因此大多数应用程序开发
人员都通过 ODBC 配置对话框中的复选框,关闭此选项,以使用第二种方法。
  在 SQL Server 7.0 中,准备/执行方法是 SQL Server 的本机功能。准备好 SQL 语句
之后,才会执行它。至于默认的结果集,应用程序只需要调用 sp_execute,提供准备操作生
成的句柄,语句就会被执行。对于游标,与其他游标处理过程看起来很相似,事实上,它也具
有相同的特性,包括如果游标是快速只前向型,还可以使用 autofetch 和 toclose。
  准备/执行操作的流程如图 5 所示。   
  图 5. 准备/执行模型
  调用存储过程
  存储过程一般是从 ODBC 和 OLE-DB,通过发送 SQL 语句给使用 ODBC 标准 CALL 语法调
  SQLExecDirect(hstm, "{call addorder(?)}", SQL_NTS)
  对于默认结果集,这是一个简单的流,因为这正是 RPC 消息原本要处理的对象。客户机
服务器发送 RPC 消息,并获取来自存储过程的处理结果。如果是游标,则情况稍微复杂一
些,客户机需要调用 Sp_cursoropen,就像其他游标一样。Sp_cursoropen 含有内部逻辑,检
测该存储过程是否只包含一条 SELECT 语句。如果是,则对该 SELECT 语句打开一个游标。如
果该存储过程中不是一条 SELECT 语句,则客户机会得到一个指示,说明"我们为您打开结果
集,但是我们将以流水的方式返回数据流,您可以把这个数据流提供给用户"。
  存储过程执行流程如图 6 所示。   
  图 6. 调用存储过程
  SQL Manager
  前面已经提到过的 SQL Manager 驱动很多服务器处理过程,它实际上是服务器的心脏。
SQL Manager 处理所有调用存储过程的请求,管理过程缓存,拥有虚拟系统存储过程,在稍后
要介绍的特定查询的自动参数化过程中也要涉及。如果您有与本文类似的描述 SQL 6.5 或更
老版本的文章,则不会读到有关 SQL 管理器的讨论,然而,您会读到一些完成 SQL 管理器
工作的一些不同的组件。但是在 SQL Server 7.0 中,这些组件被统一为 SQL 管理器,通过
  在一般情况下,当要求 SQL 管理器为您做某些工作时,通过 RPC 消息调用 SQL 管理器
。但是,当通过 SQL 消息发送 SQL 语句并进入引擎编译时,也会用到 SQL 管理器。当存
储过程或批处理程序包含 EXEC 语句时,也会调用 SQL 管理器,因为 EXEC 实际上就是调用
SQL 管理器。如果该 SQL 语句传送了下面就要讨论的一个自动参数化模板,则需要调用
SQL 管理器对该查询进行参数化处理。当特定查询语句需要装入缓存时,也要调用 SQL 管理
  编译与执行
  现在讨论在 SQL Server 中编译和执行的一般流程。需要注意的是编译和执行在 SQL
Server 内部是两个不同的阶段。SQL Server 编译查询语句和执行该语句之间的间隔时间可能
非常短,只有几个毫秒,也可能是几秒钟、几分钟、几小时甚至几天。在编译过程中(这个过
程包括优化),我们必须区分什么样的知识可以用于编译。并不是所有对编译有用的知识对执
行也起作用。您必须把编译和执行理解为两个不同的活动,即使您发送并立即执行的是特定
  当 SQL Server 可以开始处理查询语句时,SQL Manager 要在缓存内进行查找,如果没有
找到该语句,则必须编译该语句。编译处理要完成以下几件工作。首先,要进行分析和正常化
。分析就是剖析该 SQL 语句,将其转换成更适合计算机处理的数据结构。分析还要验证语法
的正确性。分析不进行表名和列名合法性等检查,这些工作在正常化阶段完成。正常化主要是
解析 SQL 语句中引用的对象,转换成实际的数据库对象,检查请求的语义是否有意义。例如
,试图执行一个表,这在语义上就是错误的。
  下一步是编译 Transact-SQL 代码。Transact-SQL 和 SQL 本身都让人有点儿困惑,
Microsoft 的开发人员也像别人一样经常互换两个词。但是,这两者之间还是有重要差别的。
SQL 包括所有 DML 语句:INSERT、UPDATE、DELETE 和 SELECT。SQL Server 还有一种包括这
些 DML 语句的语言,称为 Transact-SQL,也就是 TSQL。TSQL 提供过程结构:IF 语句、
WHILE 语句、局部变量声明等。服务器内部对 SQL 和 TSQL 的处理方法完全不同。TSQL 的过
  SQL 语句本身由典型的查询优化器来处理。优化器必须把基于集合的 SQL 语句的非过程
化的请求,翻译成可以被高效执行并返回所需结果的过程。除非特别说明,我们在以下讨论编
  上面已经提到,编译和执行是两个不同的查询处理阶段,因此,优化器完成的工作之一是
基于相当稳定的状态进行优化。您可以注意到,SQL Server 可能会根据语句所满足的条件重
新编译,所以状态并不是永远稳定的,但也不是处于不停的变化之中。如果优化器使用的信息
变化太剧烈、太经常 - 并发处理器的数量和锁的数量不稳定 - 则必须不断重新进行编译,
而一般来说编译是比较耗时的。例如,SQL 语句的运行时间为百分之一秒,而编译可能需要占
用半秒。最理想的情况是,SQL Server 能够只编译语句一次,而执行成千上万次,不必每次
  编译阶段的最终产品是查询计划,放在过程缓存中。便宜的特定 SQL 计划并不放在缓存
中,不过这只是个小问题。我们不希望缓存被不太可能重复执行的内容占满,一般来说,特定
SQL 语句的计划是最不可能反复使用的了。如果语句编译已经很便宜(小于百分之一秒),
则没有必要再把计划放入缓存,用不太可能重新使用的计划占用缓存。
  把计划放入缓存之后,SQL Manager 按照执行要求逻辑进行检查,确定是否有更改的内容
,是否需要重新编译。即使编译到执行之间时间间隔只有几毫秒,也可能有人会执行一条数据
定义语句 (DDL),为关键的表加了索引。这种可能性不大,但是确实存在,因此 SQL Server
必须考虑这一点。有几种情况 SQL Server 必须重新编译存储规划。元数据的修改,例如增加
或删除索引,是重新编译的最主要的原因。服务器必须确信所使用的计划反映了索引的当前状
  重新编译的另一种原因是统计情况发生变化。SQL Server 还维护不少数据使用频率的统
计信息。如果数据使用频率分布情况变化很大,则可能需要另一个查询计划以便更有效地执行
。SQL Server 跟踪表数据插入和删除的统计数据,如果数据修改的数量超过根据表的容量变
化的某一阈值,则需要根据新的分布数据重新编译计划。
  图 7 给出了编译和执行过程的流程。   
  图 7. 编译与执行
  注意,实际参数的改变并不会导致重新编译,环境的改变,例如可用内存的增加或所需数
  执行是比较简单的,如果需要执行的查询很简单,如"插入一行",或从带有唯一索引的表
中查询数据,则执行处理会非常简单。但是,很多查询都要求大量的内存以提高运行效率,或
至少从所增加的内存得到好处。在 SQL Server 6.5 中,每个查询能够使用的内存限制在
0.5MB 或 1MB 以下。有一个控制查询内存使用的参数,称为排序页。顾名思义,它主要是限
制可能占用大量内存的排序操作。不管要处理的排序有多大,在 SQL Server 6.5 中,内存的
使用不能超过 1MB。即使您使用的机器上配置了 2GB 内存,需要对数百万行数据排序,也不
能突破限制。显然,复杂的查询不能高效执行,因此 SQL Server 开发人员增加了 SQL
Server 7.0 的能力,使得单个查询可以使用大量的内存。
  另一个问题随之而来。一旦您开始允许查询使用大量内存,就必须确定如何把内存分配给
可能需要内存的很多查询。SQL Server 按照以下方法解决这个问题。当查询计划优化之后,
优化器要确定有关给该查询使用的内存的两部分信息。第一,该查询有效执行所需要的最小内
存,该参数与查询计划一起存放。优化器还要确定该查询可以获益的最大的内存量。例如,如
果要排序的整个表只有 100MB,分配 2GB 内存就没什么帮助了。您需要的只是 100MB,这个
  当 SQL Server 开始执行计划时,该计划被传递给一个所谓内存授权调度程序的例程中。
这个授权调度程序要完成几项有趣的工作。首先,如果授权调度程序要处理的查询在计划中没
有排序或杂凑操作,则 SQL Server 知道该查询不会需要很多内存。在这种情况下,不需要内
存授权调度程序进行判断。该计划会立即执行,因此典型的事务处理请求会完全旁路这种判断
机制。内存授权调度程序还设有多个队列处理不同容量的请求。内存调度程序优先处理较小的
请求。例如,如果有一个查询要求"提取前 10 个",并且只需要对 20 行排序,则虽然需要经
过内存授权调度程序,但是要释放该查询并且很快调度。服务器需要并行或并发执行许多这种
  如果有很大的查询,您希望一次只运行几个查询,让它们占有所需的更多内存。SQL
Server 确定一个由 4 X(系统中的 CPU 个数)得到的数。如果可能,SQL Server 会同时运
行那个数量的查询,为它们分配高效运行所需要的最小内存。如果还剩有内存,则一部分查询
会允许占用最大高效内存。SQL Server 试图既为查询分配尽可能多的内存,又让尽可能多的
  能够使用最大高效内存对某些操作很重要,例如夜间运行的批处理过程。您可能会生成很
大的报表,或重新建立索引。这些查询可能使用大量内存,这种机制可以动态调整对内存的需
求。因此,如果如果在队列中等待处理的查询不多,则内存授权调度程序会经常分配给查询最
大需要的内存。如果白天的机器负载很重,则就不能同时运行太多的查询。这些查询会得到有
  一旦调度程序说现在可以为请求分配内存,则计划即被"打开",开始实际运行。计划会一
直运行直到完成。如果查询使用了默认结果集模型,则计划会一直运行到检索到所有结果为止
,然后把结果返回给客户机。如果使用的是游标模型,则处理过程略有不同。每个客户机请求
只提取一块数据,并不是所有数据。当每个结果块返回给客户机之后,SQL Server 必须等待
客户机的下一个请求。在等待时,整个计划就会睡眠。这意味着要释放一些锁,要释放一些资
源,并保留一些断点信息。这些断点信息使得 SQL Server 能够返回到睡眠之前的状态,使得
执行可以继续。



Copyright © 2004 wanxu.com All Rights Reserved