《Microsoft Sql server 2008 Internals》读书笔记--第九章Plan Caching and Recompilation(11)

《Microsoft Sql server 2008 Internals》读书笔记订阅地址:

http://www.cnblogs.com/downmoon/category/230397.html/rss

《Microsoft Sql server 2008 Internals》索引目录:

《Microsoft Sql server 2008 Internal》读书笔记--目录索引

本文将继续关注计划向导的后半部分,包括:计划指南的管理、计划指南的考虑因素、计划指南的有效性、从计划缓存中冻结计划。这也是计划缓存和重编译的最后一节。

管理计划指南

  除了sp_Create_plan_guide和sp_get_query_template,还有一个基本的存储过程与计划指南相关:sp_control_plan_guide(http://msdn.microsoft.com/zh-cn/library/ms188733.aspx)。这个存储过程允许你删除、启用或禁用计划指南 ,语法如下:

sp_control_plan_guide [ @operation = ] N'<control_option>'
[ , [ @name = ] N'plan_guide_name' ]

<control_option>::=
{
DROP
| DROP ALL
| DISABLE
| DISABLE ALL
| ENABLE
| ENABLE ALL
}

 注意:计划指南的行为与架构绑定视图采用一种方式,即一个数据库里的任何对象计划指南引用的存储过程、触发器、函数不能被修改(alter)或删除(drop)。一旦计划指南被创建,无论为disabled或enabled,它引用的对象不能被alter或drop,只有sp_Control_plan)guide来drop这些对象。

包含计划指南的元数据视图是sys.plan_guides(http://msdn.microsoft.com/zh-cn/library/ms178010.aspx)。如果必要,你可以手工利用这个视图的信息重建计划指南定义。 

计划指南的考虑因素

对SQL Server决定是否有一个合适的计划指南被使用时,计划指南的语句文本必须匹配被编译的查询,而且是字符对字符的精确匹配。包括case,空格,和注释。正如SQL Server对待adhoc查询计划那样。如果语句文本接近,但不精确匹配,这会导致难于故障检测。当匹配一个SQL模板时,无论定义是否包含一个与之相匹配的语句,SQL Server允许在批处理的定义上更大的灵活性,特别,关键字大小写、空白、注释被忽略。

为确保你的计划指南使用被你的应用程序提交的精确文本,你可以运行SQL Server的Profiler(优化向导),以捕捉SQL:BeachCompleted 和RPC:Complete事件。在相关的批处理显示在优化器的窗上方时,你可以右键--选择额外的事件数据,保存批处理的SQL文本为文本文件。仅仅复制和粘贴是不充分的,因为不能包含其他额外的信息。

为了验证你的计划指南是否被使用,你可以查看计划的XML计划。如果你直接运行计划,你可以使用选项SET SHOWPLAN_XML ON,也可以通过跟踪捕捉showplan XML。一个XML计划有两个定义项,指示使用计划指南的查询。这两个项是PlanGuideDB和PlanGuideName,如果计划指南是一个模板计划指南,XML计划也有两个项:TemplatePlanGuideDB和TemplatePlanGuideName。

当一个计划被提交处理时,如果数据库里有任何计划指南,SQL Server首先检查是否语句匹配一个SQL计划指南或对象计划指南。查询字符串被哈希以使在数据库的已存在计划中更快找到任何匹配 的字符串,如果没有一个SQL或对象计划指南被找到,SQL Server查找一个模板计划指南,如果找到,它尝试匹配参数化的结果查询到SQL计划指南,此时你定义的其他Hint就派上用场了,比如可以使用强制参数化。整个过程如下图:

邀月工作室

关键步骤有两个:

1、对于批处理中的定义语句,SQL Server试图匹配语句到一个基于SQL的计划指南,它的@module_or_batch参数匹配新进的批处理文本,包括任何常量原义值,而它的@stmt参数也匹配批处理中的语句,该语句文本被修改为包括计划指南中定义的查询提示。随后,该语句被使用定义的提示编译。

2、如果一个计划指南在步骤一没有匹配到语句,SQL Server试图使用强制参数化来使语句参数化。在这一步中,参数化可能因为下列原因而失败:

语句已经被参数化或包含局部变量

Parameterization Simple数据库SET选项被应用,并且没有模板类型的计划指南应用到语句和Parameterization Simple查询提示

一个存在的模板类型的计划指南应用到语句和定义Parameterization Simple查询提示

我们看一个例子

USE AdventureWorks2008
GO
SELECT SpecialOfferID, COUNT(*) as Total
FROM Sales.SalesOrderDetail
GROUP BY SpecialOfferID;

 

邀月工作室

该表中有1236页,查看的方法见第六章中的视图。对于大多数的值,一个在SpeaiclaOfferID上的非聚集索引是非常有用的。我们创建一个索引:

CREATE INDEX Detail_SpecialOfferIndex ON Sales.SalesOrderDetail(SpecialOfferID);

 

我们假定非常小的查询,并且SpeaiclaOfferID的值为1或2,99%的查询是在查找不常用的值。我们宁可查询优化器自动化参数查询而访问Sales.SalesOrderDetail表,定义一个特定的SpecialOfferID。因此我们创建一个模板计划指南以自动参数化如下格式的查询:

SELECT * FROM Sales.SalesOrderDetail WHERE SpecialOfferID = 4;

 

然而,我们想要确保决定计划是否可能是某个值(该值可能使用一个聚集索引扫描)的初始化参数,也就是值1或2。我们能利用sp_get_query_template存储过程产生的自动参数化查询,首先用它创建一个模板计划指南,然后创建一个带有OPTIMIZE FOR提示的SQL计划指南。该提示强制SQL Server每次查询需要被重优化时假定一个定义

USE AdventureWorks2008;
-- Get plan template and create plan Guide
DECLARE @stmt nvarchar(max);
DECLARE @params nvarchar(max);
EXEC sp_get_query_template
N
'SELECT * FROM Sales.SalesOrderDetail WHERE SpecialOfferID = 4',
@stmt OUTPUT,
@params OUTPUT
--SELECT @stmt as statement -- show the value when debugging
--
SELECT @params as parameters -- show the value when debugging 

EXEC sp_create_plan_guide N'Template_Plan_for SpecialOfferID',
@stmt,
N
'TEMPLATE',
NULL,
@params,
N
'OPTION (PARAMETERIZATION FORCED)';

EXEC sp_create_plan_guide
@name = N'Force_Value_for_Prepared_Plan',
@stmt = @stmt,
@type = N'SQL',
@module_or_batch = NULL,
@params = @params,
@hints = N'OPTION (OPTIMIZE FOR (@0 = 4))';
GO

你可以验证是否计划被自动参数化,并被为某个使用在SpecialOfferID列上的非聚集索引的值优化:

 

DBCC FREEPROCCACHE;
SET STATISTICS IO ON;
SELECT * FROM Sales.SalesOrderDetail
WHERE SpecialOfferID = 3;
GO
SELECT * FROM Sales.SalesOrderDetail
WHERE SpecialOfferID = 4;
GO
SELECT * FROM Sales.SalesOrderDetail
WHERE SpecialOfferID = 5;
GO

你应该在Statistics IO输出窗口注意到,每个执行使用一个不同数量的读取,对应不同的行数。你也可以通过检查Statistics XML 输出使用已编译计划。如果你设置这个选项为ON,运行查询查找值5,得,XML文档中应该有一个节点类似如下:

<ParameterList>
<ColumnReference Column="@1" ParameterCompiledValue="(4)" ParameterRuntimeValue="(5)"/>
</ParameterList>

 

计划指南并不打算加速查询编译时间。不仅仅SQL Server首先决定是否计划指南可能(潜在的)匹配被编译的查询,但是被计划指南强制的计划必须是查询优化器自身提出。计划指南的好处是减少(查询优化器自身不能提出最佳查询优化器)的查询的执行时间。

在SQL Server 2008中的计划指南的主要增强是使计划指南更加可用。SQL Server 2008包含SMO和Management Studio支持。包括计划指南的脚本作为数据库的脚本的一部分。一旦计划指南被脚本化,它可以被从其他的运行相同查询的SQL Server实例中复制。

计划指南的有效性

SQL Server 2005计划指南的实施的一个不足是:可能以某种方式改变表的物理设计(如可能drop一个索引),此时可能验证一个计划指南,而任何使用该计划指南的查询将失败,无论它们是否执行。SQL Server 2008当改变表设计能侦探这种情况而分解一个计划指南。它现在能重编译没有计划指南的查询,并通过跟踪事件通知管理员。此外,还有一个新的系统函数不验证计划指南。

sys.fn_validate_plan_guide(http://msdn.microsoft.com/zh-cn/library/bb933946.aspx)

验证指定计划指南的有效性。通过验证计划指南,可确定优化器是否能够在不进行修改的情况下直接使用该指南。例如,基于函数的结果,可决定删除该计划指南并重新调整查询或修改数据库设计(例如,重新创建计划指南中指定的索引)。

示例:

SELECT * FROM sys.plan_guides pg
CROSS APPLY
(
SELECT * FROM sys.fn_validate_plan_guide(pg.plan_guide_id)) v;

 

sys.fn_validate_plan_guide 函数返回计划指南应用于其查询时遇到的第一条错误消息。如果计划指南有效,则将返回一个空的行集。在更改了数据库的物理设计后,计划指南可能会变为无效。例如,如果计划指南指定了特定索引并且随后将该索引删除,则查询将不能再使用该计划指南。你可以合并这个到系统中的任何架构改变:

BEGIN TRANSACTION;
DROP INDEX t2.myindex;
IF EXISTS(
SELECT * FROM sys.plan_guides pg
CROSS APPLY
(
SELECT * FROM sys.fn_validate_plan_guide(pg.plan_guide_id)) v
)
ROLLBACK TRANSACTION
ELSE
COMMIT TRANSACTION;

 

冻结计划缓存里的一个计划

SQL Server 2008 增加了一个新的存储过程,允许你自动地从一个已经被缓存的计划中创建一个计划指南。存储过程sp_create_plan_guide_from_handle(http://msdn.microsoft.com/zh-cn/library/bb964726.aspx

该存储过程需要一个参数plan_handle(计划指南名称作为这个参数),使用plan_handle值对应的可执行计划创建一个计划指南。调用plan freezing的能力是它允许你确保每次被查询被执行时,良好性能的计划被重用。假定我们已经找到非常理想的计划,我们愿意相信计划是在序列执行的一个:

SELECT City, StateProvinceID, PostalCode FROM Person.Address ORDER BY PostalCode DESC;

 

我们通过一个匹配查询的文本值可以在缓存中找到相关的计划:

SELECT plan_handle
FROM sys.dm_exec_query_stats AS qs CROSS APPLY sys.dm_exec_sql_text(qs.sql_handle) AS st
WHERE st.text LIKE N'SELECT City,%';

一旦我们找到plan_handle,传递这个参数给sp_create_plan_guide_from_handle如下:
EXEC sp_create_plan_guide_from_handle
@name = N'Guide1_from_XML_showplan',
@plan_handle =0x06002000D59D103310D11805000000000000000000000000

 

这儿有几种情况,计划指南和计划冻结特别有好处:

你可以使用计划指南在服务器升级后为计划回归提供一个工作区

◆你可以不允许为一个良好性能系统的关键计划的计划改变 

你可以通过冻结一个好的计划排除一个问题查询

ISVs能为运载它们的系统创建已知的好计划

你可以在测试系统优化,而携带计划指南到你的生产系统

你可以包括一个克隆数据库的计划指南。

本章小结:

对于所有的缓存机制,重用缓存计划避免重编译和优化。这节省编译时间,但这意味着无论特殊的参数值被传递,相同的计划被使用。如果对于一个给定的参数值的优化计划不同于缓存计划,优化执行时间没有得到。因为这个原因,SQL Server非常谨慎对待自动参数化。当一个应用程序使用sp_executesql,预编译和执行或存储过程时,应用程序开发者可以决定什么应该被参数化。你应该仅仅在常量化不会巨大影响优化选项的值的范围(range)使之参数化。

在本章中,我们看了被查询优化器生成的缓存和计划重用。SQL Server不仅能从存储过程中,而且可以从adhoc和自动参数化查询中缓存和重用计划。因为生成查询计划可能是昂贵的,它也能帮助理解查询计划如何和为什么查询计划被重用,以及它们什么时候必须被重生成。理解缓存和重用计划如何工作将会帮助你决定什么时候使用缓存计划可能是一个正确的选择,什么时候可能需要确保SQL Server提出一个新的计划,以使你的应用程序性能最佳。

 

下文将开始第十章《Tansactions  and Concurrency》


posted @ 2010-07-20 00:34  邀月  阅读(1907)  评论(7编辑  收藏  举报