日期:2013-01-30  浏览次数:20502 次

  两种特殊情况

  与计划最优性相关的重新编译在下列两种特殊情况中的处理方式有所不同。

特殊情况 1:在空表或索引视图上创建的统计

  SQL Server 2005 处理下列情况的方式不同于 SQL Server 2000。用户创建了一个空表 T。然后又在 T 一个或多个列上创建了一个统计 S。由于 T 为空,因此统计二进制大对象(直方图)为 NULL,但曾经在 T 上创建了统计。假设在查询编译期间已发现 S 是“令人关注的”。依据重新编译阈值的“500 行”规则,只要至少包含 500 行,T 才会在 SQL Server 2000 上导致重新编译。所以,如果 T 包含的行不足 500,用户可能使用欠优化的计划。

  SQL Server 2005 可检测到这种特殊情况,并以不同的方式进行处理。在 SQL Server 2005 中,这种表或索引视图的重新编译阈值为 1。换句话说,即便仅在 T 中插入一行,也可能导致重新编译。发生这种重新编译时,S 将被更新,同时 S 的直方图不再为 NULL。然而,这一重新编译附带了重新编译阈值 (500 + 0.20 * n)的普通规则。

  在 SQL Server 2005 中,即便发生下列情况,重新编译阈值一直为 1:(1) T 没有统计;或者 (2) T 没有在查询编译期间被认作是“令人关注的”统计。

特殊情况 2:触发器重新编译

  导致重新编译的与计划最优性相关的所有缘由都适用于触发器。另外,由于已插入或已删除的表中的行数在不同的触发器执行间发生巨大变化,也会对触发器产生与计划最优性相关的重新编译。

  回想一下,影响一行或多行的触发器会被单独缓存。已插入和已删除的表中的行数通过触发器的查询计划进行保存。这些数字反映了导致计划缓存的触发器执行的行数。如果后续的触发器执行产生了拥有“截然不同的”行数的已插入或已删除的表,那么将对该触发器进行重新编译(并缓存带有新行数的全新的查询计划)。

  在 SQL Server 2005 中,“截然不同”的定义如下:

| log10(n) – log10(m) | > 1     if m > n
| log10(n) – log10(m) | > 2.1   otherwise

  其中 n 是已缓存查询计划中的已插入或已删除表的行数,而 m 是当前的触发器执行的对应表的行数。如果同时存在“已插入”和“已删除”的表,将对两者分别执行上面提到的测试。

  举一个计算示例,从 10 到 100 的行数更改不会导致重新编译,而从 10 到 101 的更改则完全相反。

  在 SQL Server 2000 中,“截然不同”的定义如下:

| log10(n+5) – log10(m+5) | >= 1

  其中 n 和 m 的定义同上。请留意,依据这个公式,在 SQL Server 2000 中将已插入或已删除的表的基数从 5 改为 95,将导致重新编译,而从 5 到 94 的更改则不然。

识别与统计相关的重新编译

  可通过包含字符串“Statistics changed”的事件探查器跟踪(将在本文后面引见)的“EventSubClass”列来识别与统计相关的重新编译。

结束语

  与本文档的主题没有直接相关的一个问题是:给定的多个统计以相反的顺序存在于一组相反的列中,那么在查询优化期间,查询优化器如何决定所要载入的统计呢?答案并不那么简单,但查询优化器采用如下准绳:为最近的统计提供比较旧的统计更高的优先权;为使用 FULLSCAN 选项计算得出的统计提供比用样例计算得出的统计更高的优先权;等等。

  与计划最优性相关的编译、重新编译和统计创建/更新间的“因果”关系可能会形成混淆。回想一下,统计可通过手动或自动方式创建或更新。只要编译和重新编译才会导致统计的自动创建或更新。另一方面,当(手动或自动)创建或更新一个统计时,重新编译查询计划(可能会发现该统计“令人关注”)的概率将增大。

  最佳实务

  下面给出了四个用于减少与计划最优性相关的批处理重新编译的最佳实务:

  最佳实务:由于表变量的基数发生变化不会导致重新编译,所以可考虑使用表变量来替代临时表。然而,由于查询优化器不跟踪表变量的基数,同时不在表变量上创建或维护统计,因此不可能产生最佳的查询计划。用户必须确认情况能否如此,并适当地加以权衡。

  最佳实务:KEEP PLAN 查询提示可改变临时表的重新编译阈值,使之与永世表的重新编译阈值相反。所以,如果对临时表的更改会导致大量的重新编译,就可使用此查询提示。可使用下列语法指定该提示:

SELECT B.col4, sum(A.col1)
FROM dbo.PermTable A INNER JOIN #TempTable B ON A.col1 = B.col2
WHERE B.col3 < 100
GROUP BY B.col4
OPTION (KEEP PLAN)

  最佳实务:为了完全避免因与计划最优性相关的(与统计更新相关的)缘由而导致的重新编译,可使用下列语法指定 KEEPFIXED PLAN 查询提示:

SELECT c.TerritoryID, count(*) as Number, c.SalesPersonID
FROM Sales.Store s INNER JOIN Sales.Customer c
ON s.CustomerID = c.CustomerID
WHERE s.Name LIKE '%Bike%' AND c.SalesPersonID > 285
GROUP BY c.TerritoryID, c.SalesPersonID
ORDER BY Number DESC
OPTION (KEEPFIXED PLAN)

  运用这一选项,只要与正确性相关的缘由(例如,语句更改所援用的表的架构,或用 sp_recompile 过程标记的表)才会导致重新编译。

  在 SQL Server 2005 中,下方所述的行为方式略有不同。假设带有 OPTION(KEEPFIXED PLAN) 提示的查询初次被编译,而这一编译会导致统计的自动创建。如果 SQL Server 2005 可获得一个特殊的“统计锁”,那么就会发生重新编译并自动创建统计。如果无法获得“统计锁”,就会不产生重新编译,并在没有该统计的情况下编译查询。在 SQL Server 2000 中,出于与统计相关的缘由,带有 OPTION(KEEPFIXED PLAN) 的查询从不会被重新编译,所以在这种情况下,不会尝试获取“统计锁”或自动创建统计。

  最佳实务:对表或索引视图上定义的索引和统计关闭统计自动更新,将确保因这些对象所导致的与计划最优性相关的重新编译将停止。但是请留意,用这种方法关闭“自动统计”功用通常并不是一个好办法,由于查询优化器不再呼应这些对象中的数据变更,并可能导致次最佳查询计划。不到万不得已不要采用这种方法。

八、编译、重新编译和并发

  在 SQL Server 2000 中,存储过程、触发器和动态 SQL 的编译和重新编译均被串行化。例如,假定使用“EXEC dbo.SP1”提交了一个存储过程用以执行。并假设当 SQL Server 编译 SP1 时,收到了另一个援用相关存储过程的请求“EXEC dbo.SP1”。第二个请求将等到第一个请求完成 SP1 的编译,然后尝试重用结果查询计划。在 SQL Server 2005 中,编译被串行化,而重新编译则不会。换句话说,相反存储过程的两个并发重新编译可能会继续。最后结束的重新编译请求将替代由另一个请求生成的查询计划。

九、编译、重新编译和参数嗅探

  “参数嗅探”是一个过程,通过这一过程,SQL Server 的执行环境可在编译或重新编译时“嗅探”当前参数值,并将之传递给查询优化器,以用于生成更快的查询执行计划。“当前”一词指点致编译或重新编译的语句调用中所存在的参数值。在 SQL Server 2000 和 SQL Server 2005 中,将在编译或重新编译下列批处理类型时嗅探参数值:

  存储过程