渔人 发表于 2009-2-8 13:46:39

Microsoft SQL Server 查询处理器的内部机制与结构(4)

  过程缓存
  
  
  我们在前面已经多次提到 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 的管理是集中式的。
  
  
  写入缓存的除计划外,还有反映通过编译该查询实际创建该计划的成本的成本因子。如果这是一个特定计划,则 SQL Server 将它的成本设置为 0,表示可以立即将它撤出过程缓存。对于特定 SQL,虽然有可能被重复使用,但可能性很小,如果系统内存紧张,总是愿意首先撤出特定语句的计划。这样,特定查询的计划是最适合清出缓存的对象。如果查询不是特定的,则 SQL Server 会把该成本设置为实际编译查询的成本。这些成本是以磁盘 I/O 为单位的。如果从磁盘中读出一个数据页,则有一个 I/O 成本。在编译计划时,信息从磁盘中读出,包括统计数据和查询本身的文本。SQL 要进行附加的处理,而且这处理工作被正常化为 I/O 成本。现在,建立过程的成本可用执行 I/O 的成本表示。该成本非常恰当反映了,与打算用磁盘缓存的数据量相比,管理实际打算分配给存储过程和任何种类查询计划的缓存量的能力。该成本被计算出来之后,该计划就会被写入缓存。
  
  
  图 8 显示计算计划成本并将其写入缓存的流程。
   
www.ad119.cn/bbs/attachments/computer/20090208/20092813461178177801.gif

  图 8. 将计划写入缓存
  
  
  如果另一个查询可以重新使用该计划,则 SQL Server 要再次判定计划的类型。如果是一个特定计划,SQL Server 会把成本加 1。这样,如果特定计划确实要被重新使用,则它会在缓存中稍作停留,停留时间越长,成本就增加越多。如果该计划经常被重新使用,则成本会一次增加一个单位地不断增长,直到增长到其实际编译成本。该成本和设置的成本一样高。不过该计划经常被重复使用;如果同一用户或其他用户不断重新提交完全一样的 SQL 文本,该计划就会留在缓存中。
  
  
  如果查询不是特定的,也就是说是一个存储过程、带参数的查询或自动参数化的查询,则每次该计划被重新使用时,成本都会设置回原来的值。只要计划被重新使用,就会留在缓存中。即使有一段时间没有被使用,取决于最初的编译代价的高低,计划停留在缓存中的时间也有长短。
  
  
  图 9 显示从缓存中检索计划并调整成本的流程。
   
www.ad119.cn/bbs/attachments/computer/20090208/20092813461182877802.gif

  图 9. 从缓存中检索计划
  
  
  迟缓写入器(Lazywriter) 是使计划过时的机制,负责在必要的时候从缓存中删除计划。迟缓写入器实际上是存储引擎的一部分,但是因为迟缓写入器对于查询处理机制是如此重要,我们还是在这里进行讨论。迟缓写入器管理查询计划内存使用的机制与管理页面的机制一样,因为 SQL Server 7.0 计划存储在普通缓冲存储器中。迟缓写入器要检查系统中所有的缓冲器标题。如果系统的内存不紧张,检查的次数就很少;如果开始紧张,则迟缓写入器就会经常运行。当迟缓写入器运行时,它要检查缓冲区标题,并检查缓存区中该页面的当前成本。如果成本为 0,则意味着自从上次迟缓写入器检查以来,该页面没有被使用过,于是迟缓写入器就会释放该页面,以便为系统增加可用内存,用于页面 I/O 或其他计划。此外,如果该缓冲区包含过程计划,则迟缓写入器会调用 SQL 管理器,以完成一些清理工作。最后,该缓冲区会被放到可用内存表中供重新使用。
  
  
  如果与缓冲区关联的成本大于 0,则迟缓写入器会把成本减 1,并继续检查其他缓冲区。这成本实际上反映的,某计划若是没被使用,它在缓存中还能存在多少个迟缓写入器的检查周期。这种算法,除了如果对象是存储过程则调用 SQL Manager 这一步之外,对缓存中的计划和缓存的数据或索引没有什么区别。迟缓写入器并不知道对象是否存储过程,这种算法很好地平衡了磁盘 I/O 对缓存的使用和存储过程计划对缓存的使用。
  
  
  您会发现,如果计划的编译成本很高,那么即使很长一段时间都没有被重新使用,也仍然会停留在缓存中,这是因为其初始成本太高了。经常被重新使用的计划也会长期停留在缓存中,这是因为每当它被重新使用时其成本已被重新设置,迟缓写入器不会看到它的成本降为 0。
  
  
  图 10 显示迟缓写入器处理缓存的流程。
   
www.ad119.cn/bbs/attachments/computer/20090208/20092813461184377803.gif

  图 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 的连接,即使它们来自相同的查询,也不能使用相同的计划。处理过程是不同的。例如,如果一个连接把 concat_null_yields_null 设置为 ON,另一个把 concat_null_yields_null 设置为 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 文本,以此作为特定的 S <
页: [1]
查看完整版本: Microsoft SQL Server 查询处理器的内部机制与结构(4)