【分享】深入理解SQL Server查询优化器–构造执行计划(PART IV)

kinzent 2012-12-21 09:11:14
加精
深入理解SQL Server查询优化器–构造执行计划(PART I)
深入理解SQL Server查询优化器–构造执行计划(PART II)
深入理解SQL Server查询优化器–构造执行计划(PART III)

公开的影响规则的选项
其实我们经常会发现很多T-SQL的开发人员不小心就把优化器的一些优化规则给禁用了。

下表的Join hint { LOOP | HASH | MERGE | REMOTE } 和 查询 hint 就是经常被禁用的。



例如,一个逻辑的GROUP BY操作(包括了DISTINCT)对应的物理操作可能是一个hash 聚合 或者一个 流聚合。这两个规则分别叫做GbAggToHS (Group By Aggregate to Hash) 和GbAggToStrm (Group By Aggregate to Stream)。正常情况下,这两个规则选项对优化器来说都是可以用的,优化器在优化一个逻辑操作树时,通常会在其中选择一个。

当查询hint OPTION(HASH GROUP)出现在一个查询语句的时候,GbAggToStrm 实现规则就被禁用了。这意味着,所有的GROUP BY操作都被实现为GbAggToHS,也就是HASH聚合。

同样,join hint “INNER MERGE JOIN“ 在禁止了nested loop和hash join的规则后才可以被使用。有意思的是,在使用了join hint后,整个查询看起来好像也会呗指定了OPTION(FORCE ORDER)这个hint。

如果我们想要优化器仅仅考虑使用merge和hash join来作为一个查询的策略。我们可以使用查询hint OPTION(MERGE JOIN,HASH JOIN)。这是通过禁止了JNtoNL规则(JOIN to Nested Loops)来实现的,这样优化器就只有sort-merge和hash 连接可以选择(JNtoSM 和 JNtoHS)。使用了连接hint的同时也执行了OPTION (FORCE ORDER).

未公开的特性
警告:以下提供的信息只是用于学习目的,切勿用于生产环境,否则会产生不可预料的后果。

使用Join hint和查询hint并不能让优化器选择所有可要的规则,但我们却可以通过一些未公开的DBCC命令和未公开的DMV( sys.dm_exec_query_transformation_stats)来改变优化器选择规则的方式。

以下的内容在SQL Server 2008和2005上已经验证通过,对应的版本是x86 Developer Editions 10.0.2775 (2008 SP1 CU8) 是9.0.4294 (2005 SP3 CU9)。在其他版本可能并不一定有用。

Trace Flags 和 DBCC 命令
和其他未公开的DBCC选项一样,我们为当前会话需要使能trace flag 3604,这样输出就能显示在像SMSS这样的客户端上了。



DBCC TRACEON (3604);

我们可以通过DBCC RULEOFF来关闭一个或者多个优化器规则。这个命令接受一个或者多个规则名字作为参数。例如,为了关闭让逻辑操作变为sort-merge和hash 连接的规则,我们可以执行:



DBCC RULEOFF('JNtoSM', 'JNtoHS');

要开启某个规则,我们可以使用DBCC RULEON,也是同样的语法:



DBCC RULEON('JNtoSM', 'JNtoHS');



RULEON和RULEOFF都会返回确认的消息(在开启了trace flag 3604)。但只会影响优化器对当前会话的工作。但受我们开启和关闭DBCC开关的影响,所产生的执行计划可能不是最优的,但也会像正常的执行计划被缓存起来。切记,这些开关最好在个人的测试环境上运行。

要返回到正常的操作,打开被关闭的规则,或者简单的断开并重连服务器。也可以通过运行DBCC FREEPROCCACHE命令来移除将任何次优的执行计划从缓存中移除。

想要看当前哪些规则被打开和关闭,使用DBCC SHOWONRULES和DBCC SHOWOFFRULES命令。这两个命令都不需要参数。

SQL Server 2005 的BUG。
在SQL Server 2005上,SHOWOFFRULES会打印一些列被打开的规则,而SHOWONRULES却打印那些被关闭的规则,二者正好相反。

DBCC SHOWONRULES也不会立即返回任何结果,除非你接着运行DBCC SHOWOFFRULES。被关闭的规则会在SHOWOFFRULES(显示的是打开的规则)之前。

被关闭的规则列表在2005上同样没有被正确的格式化:所有的规则名字都连接在一起了。

在2008上面,这些就没有什么问题。

综合展示
目前,我们已经有了用来产生前面文章中贴出来的部分优化的执行计划的工具。我们可以使用sys.dm_exec_query_transformation_stats DVM来标识出被优化器调用的规则,然后使用新的DBCC 命令来有选择性的关闭一些规则,查看对最后执行计划的影响。

下面是我们一直在AdventureWorks数据库上展示的例子的逻辑关系操作树:



和最开始展示的执行计划:



该执行计划有一个预估的成本值为3.59557,而最终优化后的执行计划的预估执行成本为0.0248906:



生成部分优化的执行计划
使用第3篇文章里展示的DMV和查询,我们可以发现优化器使用了超过20种不同的规则来寻找一个充分优化的执行计划。为了生成非常基础的执行计划,我们可以只开启四个所需的核心规则(2005上需要5个)。

-- Route messages to the client
DBCC TRACEON (3604);

-- Ensure the four core implementation rules are available
DBCC RULEON ('GetToScan');
DBCC RULEON ('JNtoNL');
DBCC RULEON ('SelectToFilter');
DBCC RULEON ('GbAggToStrm')

-- Required by SQL Server 2005 only
DBCC RULEON ('ReduceGbAgg');

我们需要关闭其他正常情况下会被考虑使用的规则:


-- Alternative join implementations
DBCC RULEOFF ('JNtoHS');
DBCC RULEOFF ('JNtoSM');

-- Index-related transformations
DBCC RULEOFF ('GetIdxToRng');
DBCC RULEOFF ('GetToIdxScan');
DBCC RULEOFF ('SelIdxToRng');
DBCC RULEOFF ('SelToIdxStrategy');
DBCC RULEOFF ('SELonJN');
DBCC RULEOFF ('JNtoIdxLookup');
DBCC RULEOFF ('AppIdxToApp');
DBCC RULEOFF ('SelResToFilter');
DBCC RULEOFF ('WCJNonSELtoIdxLookup');

-- Exploration rules
DBCC RULEOFF ('GbAggToHS')
DBCC RULEOFF ('JoinCommute');
DBCC RULEOFF ('GbAggBeforeJoin');
DBCC RULEOFF ('GenLGAgg');
DBCC RULEOFF ('BuildSpool');
DBCC RULEOFF ('ImplRestrRemap');
DBCC RULEOFF ('EnforceSort');
DBCC RULEOFF ('NormalizeGbAgg');

现在我们可以得到非常基础的执行计划了:


SELECT  P.ProductNumber, 
P.ProductID,
total_qty = SUM(I.Quantity)
FROM Production.Product P
JOIN Production.ProductInventory I
ON I.ProductID = P.ProductID
WHERE P.ProductNumber LIKE N'T%'
GROUP BY
P.ProductID,
P.ProductNumber
OPTION (RECOMPILE);

因为我们关闭了很多重要的规则,因此我们将无法运行在第3篇文章中展示的DMV批处理代码。优化器也无法生成任何正确的执行计划,因为被我们禁用掉了很多规则,所以会返回如下错误:

.Net SqlClient Data Provider: Msg 8624, Level 16, State 1, Line 1 
Internal Query Processor Error: The query processor could not produce a query plan.
For more information, contact Customer Support Services.


如果你想要查看DMV的统计,你需要先清除执行计划缓存(使用DBCC FREEPROCCACHE),然后运行如下修改后的代码:

-- Clear the snapshot
TRUNCATE TABLE #Snapshot;

-- Save a snapshot of the DMV
INSERT #Snapshot
(
name,
promise_total,
promised,
built_substitute,
succeeded
)
SELECT name,
promise_total,
promised,
built_substitute,
succeeded
FROM sys.dm_exec_query_transformation_stats
OPTION (KEEPFIXED PLAN);

-- Query under test
-- Must use OPTION (RECOMPILE)
SELECT P.ProductNumber,
P.ProductID,
total_qty = SUM(I.Quantity)
FROM Production.Product P
JOIN Production.ProductInventory I
ON I.ProductID = P.ProductID
WHERE P.ProductNumber LIKE N'T%'
GROUP BY
P.ProductID,
P.ProductNumber
OPTION (RECOMPILE);
GO
-- Results
SELECT QTS.name,
promise = QTS.promised - S.promised,
promise_value_avg =
CASE
WHEN QTS.promised = S.promised
THEN 0
ELSE
(QTS.promise_total - S.promise_total) /
(QTS.promised - S.promised)
END,
built = QTS.built_substitute - S.built_substitute,
success = QTS.succeeded - S.succeeded
FROM #Snapshot S
JOIN sys.dm_exec_query_transformation_stats QTS
ON QTS.name = S.name
WHERE QTS.succeeded != S.succeeded
OPTION (KEEPFIXED PLAN);

可以看到类似如下的结果:



对这些结果的输出,可以看第3篇文章的描述。

Spool规则
接下来,让我们开启BuildSpool的规则来产生更多可选择的执行计划。这是哪些可以引入Table Spool操作符的其中一条规则,使用Table Spool可以提供效率:


DBCC RULEON ('BuildSpool');

大家自己的查询看到的执行计划可能如下:



当然,这仍然是一个不好的计划,但在嵌套循环的里面引入了Lazy Spool后,已经减少了执行计划的预估成本值,从3.59557到3.12199,是一个可观的改善。

清理工作
不要忘了恢复环境,把被关闭的规则给打开。

DBCC RULEON ('JNtoHS');
DBCC RULEON ('JNtoSM');
DBCC RULEON ('GetIdxToRng');
DBCC RULEON ('GetToIdxScan');
DBCC RULEON ('SelIdxToRng');
DBCC RULEON ('SelToIdxStrategy');
DBCC RULEON ('SELonJN');
DBCC RULEON ('JNtoIdxLookup');
DBCC RULEON ('AppIdxToApp');
DBCC RULEON ('SelResToFilter');
DBCC RULEON ('WCJNonSELtoIdxLookup');
DBCC RULEON ('GbAggToHS')
DBCC RULEON ('JoinCommute');
DBCC RULEON ('GbAggBeforeJoin');
DBCC RULEON ('GenLGAgg');
DBCC RULEON ('BuildSpool');
DBCC RULEON ('ImplRestrRemap');
DBCC RULEON ('EnforceSort');
DBCC RULEON ('NormalizeGbAgg');
DBCC RULEON ('ReduceGbAgg');


你可以通过运行DBCC SHOWOFFRULES来检查是否所有的规则都被打开了(2005刚好相反)。

最后
你可能会在想,了解这些是否在实际工作中会有帮助。答案是肯定的,只有我们了解了一个东西的内部运行和原理,我们才能更好的使用它。

除非注明,本站文章均为原创或编译,转载请注明: 文章来自sqlpub.net

...全文
3348 47 打赏 收藏 转发到动态 举报
写回复
用AI写文章
47 条回复
切换为时间正序
请发表友善的回复…
发表回复
xiaoxiangqing 2012-12-29
  • 打赏
  • 举报
回复
有点高深,以后再研究
adaly0102 2012-12-28
  • 打赏
  • 举报
回复
非常感谢,
yishanlin 2012-12-28
  • 打赏
  • 举报
回复
不错,学习了,
十一文 2012-12-28
  • 打赏
  • 举报
回复
好东西 谢谢分享
h616994528 2012-12-28
  • 打赏
  • 举报
回复
学习下~~~~
baozi135246 2012-12-26
  • 打赏
  • 举报
回复
学习学习~
  • 打赏
  • 举报
回复
慢慢看,谢谢楼主
jAmEs_ 2012-12-25
  • 打赏
  • 举报
回复
引用 3 楼 geng_ping_ping 的回复:
高端!
+1
hzh_xu 2012-12-24
  • 打赏
  • 举报
回复
感谢楼主分享,精彩
dxaxin5033 2012-12-24
  • 打赏
  • 举报
回复
谢谢,,回头看
fuxiaoyang13 2012-12-24
  • 打赏
  • 举报
回复
学习学习!!!!!!!!!!!
勿勿 2012-12-24
  • 打赏
  • 举报
回复
mark 回头看
kaikai326 2012-12-24
  • 打赏
  • 举报
回复
学习了,很有帮助
Sherry5566 2012-12-24
  • 打赏
  • 举报
回复
可是我好多不懂哦。。。
Yoncy407 2012-12-23
  • 打赏
  • 举报
回复
学习了,不错,值得收藏
jichaoyz 2012-12-23
  • 打赏
  • 举报
回复
不错来学习一下
ryback007 2012-12-23
  • 打赏
  • 举报
回复
不错,值得一看
qmy168 2012-12-23
  • 打赏
  • 举报
回复
了一些知识
anymore_1 2012-12-23
  • 打赏
  • 举报
回复
ChinaITOldMan 2012-12-23
  • 打赏
  • 举报
回复
Very Great
加载更多回复(19)

34,590

社区成员

发帖
与我相关
我的任务
社区描述
MS-SQL Server相关内容讨论专区
社区管理员
  • 基础类社区
  • 二月十六
  • 卖水果的net
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧