设为首页
加入收藏夹
Microsoft SQL Server 查询处理器的内部
浏览选项:
大
中
小
颜色
默认
灰度
橄榄色
绿色
蓝色
褐色
红色
过程缓存
我们在前面已经多次提到 SQL Server 的过程缓存。需要注意的是,SQL Server
7.0 的过程缓存与以前的版本有很大不同。在早期的版本中,有两个有效配置值用于控制过程
缓存的容量:一个是定义 SQL Server 总可用内存的固定容量,另一个是供存储查询计划使用
的内存百分比(扣除满足固定需要的内存)。在老版本中,特定 SQL 语句从不存入缓存,只
有存储过程计划才存入其中。在 SQL Server 7.0 中,内存的总容量是动态的,用于查询计划
在处理查询时,SQL Server 7.0 首先会问的是:这个查询既是特定的又是易于编译的吗
?如果是,SQL Server 就根本不会将其写入缓存中。将来重新编译这些计划比把复杂的计划
或数据页推出内存更合算。如果查询不是特定的或不易于编译,则 SQL Server 会从缓存区中
分配一些缓存内存存储该计划,因为该缓存区是 SQL Server 7.0 用来满足 99% 内存需求的
唯一来源。在少数特殊情况下,SQL Server 会直接从操作系统中分配大块内存,但是这种情
写入缓存的除计划外,还有反映通过编译该查询实际创建该计划的成本的成本因子。如果
这是一个特定计划,则 SQL Server 将它的成本设置为 0,表示可以立即将它撤出过程缓存。
对于特定 SQL,虽然有可能被重复使用,但可能性很小,如果系统内存紧张,总是愿意首先撤
出特定语句的计划。这样,特定查询的计划是最适合清出缓存的对象。如果查询不是特定的,
则 SQL Server 会把该成本设置为实际编译查询的成本。这些成本是以磁盘 I/O 为单位的。
如果从磁盘中读出一个数据页,则有一个 I/O 成本。在编译计划时,信息从磁盘中读出,包
括统计数据和查询本身的文本。SQL 要进行附加的处理,而且这处理工作被正常化为 I/O 成
本。现在,建立过程的成本可用执行 I/O 的成本表示。该成本非常恰当反映了,与打算用磁
盘缓存的数据量相比,管理实际打算分配给存储过程和任何种类查询计划的缓存量的能力。该
图 8 显示计算计划成本并将其写入缓存的流程。
图 8. 将计划写入缓存
如果另一个查询可以重新使用该计划,则 SQL Server 要再次判定计划的类型。如果是一
个特定计划,SQL Server 会把成本加 1。这样,如果特定计划确实要被重新使用,则它会在
缓存中稍作停留,停留时间越长,成本就增加越多。如果该计划经常被重新使用,则成本会一
次增加一个单位地不断增长,直到增长到其实际编译成本。该成本和设置的成本一样高。不过
该计划经常被重复使用;如果同一用户或其他用户不断重新提交完全一样的 SQL 文本,该计
如果查询不是特定的,也就是说是一个存储过程、带参数的查询或自动参数化的查询,则
每次该计划被重新使用时,成本都会设置回原来的值。只要计划被重新使用,就会留在缓存中
。即使有一段时间没有被使用,取决于最初的编译代价的高低,计划停留在缓存中的时间也有
图 9 显示从缓存中检索计划并调整成本的流程。
图 9. 从缓存中检索计划
迟缓写入器(Lazywriter) 是使计划过时的机制,负责在必要的时候从缓存中删除计划。
迟缓写入器实际上是存储引擎的一部分,但是因为迟缓写入器对于查询处理机制是如此重要,
我们还是在这里进行讨论。迟缓写入器管理查询计划内存使用的机制与管理页面的机制一样,
因为 SQL Server 7.0 计划存储在普通缓冲存储器中。迟缓写入器要检查系统中所有的缓冲器
标题。如果系统的内存不紧张,检查的次数就很少;如果开始紧张,则迟缓写入器就会经常运
行。当迟缓写入器运行时,它要检查缓冲区标题,并检查缓存区中该页面的当前成本。如果成
本为 0,则意味着自从上次迟缓写入器检查以来,该页面没有被使用过,于是迟缓写入器就会
释放该页面,以便为系统增加可用内存,用于页面 I/O 或其他计划。此外,如果该缓冲区包
含过程计划,则迟缓写入器会调用 SQL 管理器,以完成一些清理工作。最后,该缓冲区会被
如果与缓冲区关联的成本大于 0,则迟缓写入器会把成本减 1,并继续检查其他缓冲区。
这成本实际上反映的,某计划若是没被使用,它在缓存中还能存在多少个迟缓写入器的检查周
期。这种算法,除了如果对象是存储过程则调用 SQL Manager 这一步之外,对缓存中的计划
和缓存的数据或索引没有什么区别。迟缓写入器并不知道对象是否存储过程,这种算法很好地
平衡了磁盘 I/O 对缓存的使用和存储过程计划对缓存的使用。
您会发现,如果计划的编译成本很高,那么即使很长一段时间都没有被重新使用,也仍然
会停留在缓存中,这是因为其初始成本太高了。经常被重新使用的计划也会长期停留在缓存中
,这是因为每当它被重新使用时其成本已被重新设置,迟缓写入器不会看到它的成本降为 0。
图 10 显示迟缓写入器处理缓存的流程。
图 10. 迟缓写入器处理缓存的流程
处理客户机的 SQL
下面再看看提交 SQL 语句之后的处理过程。首先,我们将研究客户机向 SQL Server 发
送 RPC 事件。因为 SQL Server 收到了 RPC 事件,所以它会知道该事件是某种参数化的 SQL
;它是准备/执行模型,或者是 EXECUTESQL。SQL Server 需要构建一个缓存键,以标识这个
具体的 SQL Server 文本。如果 SQL Server 处理的是实际的存储过程,则不需要建立它自己
的键;直接使用该过程的名称即可。对于通过 RPC 调用发来的简单 SQL 文本,则通过杂凑
该 SQL 文本来建立缓存键。此外,该键还要反映一定的状态信息,如某些 ANSI 设置。使所
有 ANSI 设置为 ON 的连接和另一个使所有 ANSI 设置为 OFF 的连接,即使它们来自相同的
查询,也不能使用相同的计划。处理过程是不同的。例如,如果一个连接把 c
oncat__yields_ 设置为 ON,另一个把 concat__yields_ 设置为 OFF 的
连接,即使它们执行的是完全相同的 SQL 文本,但所产生的结果则完全不同。这样,SQL
Server 可能需要在缓存中保存计划的多个版本,每个版本对应于一个不同的 ANSI 设置组合
。启用的选项设置是键的一部分,而键字是使用这种缓存处理机制检查对象的核心,因此
SQL Server 建立这种键并用来检查缓存。如果在缓存中没有发现该计划,则 SQL Server 会
按照前面介绍的方式编译该计划,并把该计划与键一起存入缓存中。
SQL Server 还需要确定该命令是否是准备操作,这意味着该计划应该只编译但不执行。
如果是准备操作,则 SQL Server 会给客户机返回一个句柄,供客户机在以后检索并执行该计
划。如果不是一个准备操作,则 SQL Server 提取并执行该计划,就像最初从缓存中找到该计
准备/执行模型为缓存管理增加了复杂因素。预备给出了今后能够执行该计划的句柄。应
用程序可以在几小时或几天之内保持该句柄是激活的,以定期执行计划。即使需要在缓存中为
更多的活动计划或数据页面腾出空间,也不能使该句柄无效。SQL Server 实际所做的就是将
计划放入缓存,此外还从预备操作中将 SQL 保存到更加紧凑的空间。如果空间紧张,则可按
前述的方式释放计划所占用的空间,但仍有 SQL 的副本准备着。如果客户机要执行预备的
SQL,但在缓存中没有找到计划,则 SQL Server 能够检索到该文本并编译它,再将它放回缓
存中。这样,缓存中的 16 千字节 (KB) 或更多的页面用来保存可重用的计划,而长期占用的
空间或许是存储在其他处的 SQL 代码的 100 或 200 字节。
处理来自客户机的语句时的另一种情况是,查询是作为 SQL 语言事件出现的。除了一点
以外,此流程并无太大的差异。在这种情况下,SQL Server 试图使用称为自动参数化的技术
。SQL 文本与自动参数化模板相匹配。自动参数化是个棘手的问题,因此,过去一直能够利用
共享的 SQL 的其他数据库管理产品, 一般并没有提供这一选项。随之而来的问题是,如果
SQL Server 自动地参数化每个查询,那么对于随后提交的某些特定值而言,这些查询中的某
些(或绝大多数)将获得非常糟糕的计划。在程序员将参数标记放在代码之中的场合下,其假
定是程序员知道所期望的值的范围,并愿意接受 SQL Server 提供的计划。但当程序员实际补
充一个特定的值,并且 SQL Server 决定将该值当做一个可变的参数来对待时,所产生的任何
适合于某个值的计划可能不适合于后续的值。利用存储过程,通过在过程中放入 WITH
RECOMPILE 选项,程序员可以强制产生新的计划。利用自动参数化,程序员无法指出必须为每
当 SQL Server 处理自动参数化时,它是非常保守的。被安全地自动参数化的查询有一个
模板,并且只有匹配模板的查询才能应用自动参数化。例如,假设有这样一个查询,其中包含
带有等于操作符、但没有连接的 WHERE 子句,WHERE 子句中的列带有唯一的索引。SQL
Server 知道绝对不会返回一行以上,而且计划将总是使用那个唯一的索引。SQL Server 绝
对不会考虑扫描,实际值绝对不会以任何方式改变计划。对于自动参数化而言,这种查询是安
如果查询匹配自动参数化模板,则 SQL Server 自动用参数标记(例如 @p1、@p2)代替
文字,并且这就是我们发送到
服务器
的内容,正如它是 sp_executesql 调用一样。如果 SQL
Server 认为该查询对自动参数化并不安全,则客户机将向 SQL Server 发送文字的 SQL 文
图 11 显示客户机向 SQL Server 发送请求时的处理流程。
图 11. 处理客户机的 SQL
编译
现在让我们更详细地讨论一下编译和优化。在编译过程中,SQL Server 分析语句,并创
建所谓的次序树,即语句的内部表述。这是 SQL Server 6.5 实际保留在 SQL Server 7.0 中
的几个数据结构之一。该次序树是正常化的。正常化程序的主要功能是执行绑定。绑定包括检
验表和列的存在,以及装载有关表和列的元数据。有关必需的(隐含的)转换信息也附加在次
序树上,例如,如果查询试图向数字值添加整数 10,则 SQL Server 将向该树插入隐含的转
换。正常化还用视图的定义代替对该视图的引用。最后,正常化执行一些基于语法的优化。如
果该语句是传统的 SQL 语句,则 SQL Server 从关于该查询的次序树中提取信息,并创建称
为查询图表的特殊结构,设置查询图表是为了使优化器工作非常有效。然后优化该查询图表,
图 12 显示编译过程流程。
图 12. 编译
优化
SQL Server 优化器其实是由独立的段组成的。第一段是一个非基于成本的优化器,称为
琐细计划优化。琐细计划优化的完整概念是,当 SQL 语句确实只有一个可变计划时,基于成
本的优化太昂贵了。最好的例子是,带 VALUES 子句的 INSERT 语句组成的查询。它只可能有
一个计划。另一个例子是,所有的列都在唯一的封面索引(且没有其他列的索引)中的
SELECT 语句。这两例中,SQL Server 只要简单地生成一个计划,用不着在多个计划选一个更
好的方案。琐细计划优化器可找到真正显而易见的计划,而且通常非常便宜。所以,最简单的
查询在处理的前期就趋于被清除,优化器不花很多时间来搜索一个好计划。这是好事,因为随
着 SQL Server 将杂凑连接、合并连接和索引相交增加到其处理技术列表上,SQL Server
如果琐细计划优化器不能找到一个计划,SQL Server 便进入优化的下一部分,称为简化
。简化是查询本身的语法变换,寻找可交换的特性和可重新排列的运算。SQL Server 可进行
常数合并,以及无需考虑成本或分析索引是什么但能得出更有效查询的其他运算。SQL
Server 然后上载关于索引和列的统计信息,并输入优化的最后的主要部分,即基于成本的优
基于成本的优化有三个阶段。第一个基于成本的阶段,称为交易处理阶段,查找简单请求
的计划,即典型的交易处理系统。这些请求一般比由琐细计划优化器处理的那些请求要复杂些
,并要求比较众多计划查找出成本最低的计划。当交易处理阶段完成时,SQL Server 便将找
到的成本最低的计划与内部阈值进行比较。阈值用于决定是否要求进一步的优化。如果计划成
本比阈值低,那么,进行附加优化比只执行已找到的计划成本要高。所以,SQL Server 不做
进一步优化,并使用交易处理阶段找到的计划。
如果交易处理阶段找到的计划,仍比该阶段的阈值贵,SQL Server 便进入第二个阶段。
这个阶段有时称为 QuickPlan 阶段。QuickPlan 阶段扩大搜索范围来寻找一个好计划,包括
选择好的、适度复杂的查询。QuickPlan 检查可能的计划范围,完成之后,将最佳计划的成本
与第二个阈值进行比较。因为在交易处理阶段,如果发现了一个成本比阈值低的计划,优化便
终止,并使用那个计划。一般来说,SQL Server 6.5 版中已有的查询的计划,在 SQL
Server 7.0 版中也应当是最佳的,这个计划将要么被琐细计划优化器找到,要么被基于成本
的优化的头两个阶段中的一个发现。这些规则被有意地组织起来以达到这个目的。这个计划将
优化的最后阶段,称为完全优化,旨在对复杂和非常复杂的查询产生一个好计划。对复杂
的查询来说,QuickPlan 产生的计划,经常被认为比继续搜索一个更好的计划要昂贵得多,而
完全优化将被执行。在完全优化中,实际上有两个适用的独立选择。如果 QuickPlan 阶段产
生的最佳成本比"并行成本阈值"的配置值要高,并且如果
服务器
是一个多处理器机器,那么优
化器的最后阶段将涉及查找一个能在多个处理器上并行运行的计划。如果 QuickPlan 阶段的
最佳计划的成本比配置的"并行成本阈值"低,那么,优化器将只考虑串行计划。完全优化阶段
能执行各种可能性,而且很耗时,因为在这最后阶段必须找到一个计划。优化器仍可能没有检
查每个可得到的计划,因为它将任何潜在的计划成本与优化中得出此结果的成本进行比较,并
且它估算继续试用不同优化的可能成本。在某些情况下,优化器可能认为,使用现有的计划比
继续查找更优方案还要便宜,而且支付继续优化的附加编译成本将不具备高的成本效率比。在
这最后阶段处理的各种查询的计划一般只使用一次,所以,几乎没有这样的机会:为编译和优
化所付出的额外代价,会在后续执行的计划重用中一次结清。那些后续执行很可能不会发生。
找到一个计划后,该计划便变为优化器的输出,然后 SQL Server 在执行该计划之前,遍
历前面已讨论过的全部缓存机制。您应该意识到,如果完全优化阶段产生了该查询的并行计划
,并不一定意味着该计划将在多个处理器上执行。如果机器很忙,而且不支持在多个 CPU 上
图 13 显示了优化器的处理流程。
图 13. 优化
执行
查询处理的最后一步是执行。除了这一小段外,我们不会再讨论执行的详细过程。执行引
擎采用优化器生成的计划,并执行之。处理实际执行以外,执行引擎还为要运行的处理器调度
摘要
如前所述,SQL Server 的内部机制与结构是一个非常大的主题,远远超过了我们能在本
文中提供的内容。我们重在直接介绍 SQL Server 与客户机的交互方式,以及 SQL Server 关
系引擎如何处理来自客户机的请求。我们希望,在了解 SQL Server 如何处理查询,以及如何
和何时编译或重新编译它们之后,您就能利用 SQL Server 7.0 的功能和技巧编写出更好的应
用程序。
Copyright © 2004
wanxu
.com
All Rights Reserved