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

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

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

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

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

上文主要介绍了缓存大小管理、缓存项的成本(Costing of Cache entries),本文我们继续关注计划缓存的时机和计划缓存冲突。

 

缓存里的多计划

 SQL Server尽量限制一个查询或一个过程的计划的数量。因为计划是可重入的(reentrant),因而是易于完成(accomplish)的。你应该意识到某些情况下相同的过程会引起多个查询计划被存储在缓存中。最可能的情况是SET选项的不同所致。这在前文已经讨论过。

还有一种情况会影响计划是否能够被重用。如果owner名称可能被隐式地解析,则该计划不能被重用。假定,用户Sue发布这样的语句:

select * from Orders

SQL Server首先会试图通过查找在用户Sue默认的架构中名称为Orders的对象,如果未找到,则继续在dbo架构下查找Orders对象。如果用户dan执行同样的查询,则完全在另外一个流程下进行。即(dan架构->dbo架构),因此,Sue和dan不能共享这个生成的计划。因为使用不修饰的对象名,查询处理器没有假定一个已存在的计划可以重用。然而,如下语句则完全不同:

select * from dbo.Orders

此时,岐义消除,任何执行该精确计划的查询执行时总引用相同的对象。在sys.syscacheobjects视图中,列uid展示计划在连接中生成的User ID。对adhoc查询,任何使用相同的User ID的其他的连接均可使用相同的计划。一个例外情况是如果User ID值在syscacheobjects中被记录为-2,那么,提交的查询将不依赖于隐式名称的决定而可以在不同的用户间共享。这是一个首要的优先的方法。

提示:强烈推荐对象的修饰包含架构名,这样,就可以不必依赖于隐式决定,从而使计划缓存的重用更加有效。


何时使用存储过程和其他缓存机制

当你使用存储过程或其他查询机制的一种时,请牢记以下几点:

存储过程 这些对象应该在多个连接在参数已知的前提下执行批处理时使用。它们在你需要控制代码块被编译时非常有用。

Adhoc缓存 该选项仅仅在某些场景下有效。如果你设计一个应用程序期待这个行为能控制理想计划的重用并不靠谱。 

简单或强制参数化 该选项对不轻易修改的应用程序会非常有用。然而,建议你在初始设计应用程序时允许使用声明参数和数据类型。可以参看后面两个建议。

sp_executesql存储过程 该存储过程在相同的批处理可能会被多次使用且参数已知时非常有用。

The preapred and execute方法 这此方法适宜在多用户在参数已知的前提下执行批处理时,或单用户确定计划多次时。 


解决计划缓存冲突

计划缓存的使用和管理自地址问题开始。你必须判定已经存在的问题是否因计划缓存冲突引起。可能因为不当使用或不当管理计划缓存或不适合的重编译而引起的性能问题,会清楚展现为简化形式,又如产量减少,查询的响应时间增加。缓存问题也会因为不同条件的排序而显示内存溢出或连接超时。

Wait Statistics Indicating Plan Cache Problems

为判定计划缓存行为是否引起问题,第一件事情是看SQL Server中的Wait Statistics对象Wait Statistics 性能对象包含报告有关等待状态的性能计数器。在第十章(事务与并发)会有介绍。关于wait statistics对象,请看technet:http://technet.microsoft.com/zh-cn/library/ms190732.aspx
wait statistics通过sys.dm_os_wait_stats视图显示,如下语句显示SQL Server服务可能必须等待的资源列表,等待时间最长的排前:

邀月工作室

关于sys.dm_os_wait_stats视图的详细用法,请查看:http://technet.microsoft.com/zh-cn/library/ms179984.aspx,注意:sys.dm_os_wait_stats 显示已经完成的等待的时间。此动态管理视图不显示当前等待。这些统计信息在每次重新启动 SQL Server 时都不能持续存在,并且所有的数据均为自上次重置统计信息或启动服务器以来累积的数据。你应该意识到视图中显示的值都是累积的,因此,如果你需要了解在特定时间段的资源,你必须在起止时间分别轮询视图。 此动态管理视图的内容可通过运行以下命令来重置(该命令将所有计数器重置为 0)

 

DBCC SQLPERF ('sys.dm_os_wait_stats', CLEAR);
GO

执行查询期间的特定等待时间类型可以说明查询中存在瓶颈或失效点。同样,如果服务器级的等待时间较长或等待计数较多,说明服务器实例内交互查询交互中存在瓶颈或热点。例如,锁等待指示查询争用数据;页 IO 闩锁等待指示 IO 响应时间较慢;页闩锁更新指示表示文件布局不正确。

 如果你看到类型为下列任何类型资源之一相当长的等待时间或出现在列表查询结果中的顶部,则你应该调查你的计划缓存使用: 

CMEMTHREAD waits   这个wait类型显示在缓存描述符被分配时的内存对象的竞争。当某任务正在等待线程安全内存对象时出现。当多项任务尝试分配来自同一个内存对象的内存而导致出现争用时,便可能延长等待时间。一个项插入到计划缓存的高比率(rate)可能引起竞争问题。类似地,竞争也可能发生在项被从缓存中移除和资源监视线程被阻塞时。在描述符被分配的内存对象只有一个是线程安全的。正如我们所看到的,仅仅有一个针对adhoc编译计划的单个缓存存储。

考虑到相同的存储过程可能被调用几十到数百次,记住SQL Server将缓存adhoc shell查询,其中包含为每一个独立的存储过程的调用的实际参数。即使该存储过程仅有一个缓存计划。当SQL Server开始感受内存压力时,为每一个独立调用存储过程的项的插入将可能引起过度等待结果产量下降,甚至内存错误。

SOS_RESERVEDMEMBLOCKLIST waits  这个wait类型显示带大量参数的查询的缓存计划,或在IN子句带大量的值。这种查询类型需要SQL Server分配大的单元。我们称为multipage allocations。你可以查看sys.dm_os_memory_cache_counters视图。关于这个视图的详细用法,请看:http://technet.microsoft.com/zh-cn/library/ms188760.aspx

 

SELECT name, type, single_pages_kb, multi_pages_kb,
single_pages_in_use_kb, multi_pages_in_use_kb
FROM sys.dm_os_memory_cache_counters
WHERE type = 'CACHESTORE_SQLCP' OR type = 'CACHESTORE_OBJCP';

邀月工作室

RESOURCE_SEMAPHORE_QUERY_COMPILE waits   这个wait类型显示大数量的同时编译。为了防止查询内存的低效使用,SQL Server 2008限定(需要额外内存的)同时编译操作的数量。如果你看到一个RESOURCE_SEMAPHORE_QUERY_COMPILE的较高的值,你可以通过sys.dm_exec_cached_plans检查缓存计划的项:

SELECT usecounts, cacheobjtype, objtype, bucketid, text
FROM sys.dm_exec_cached_plans
CROSS APPLY sys.dm_exec_sql_text(plan_handle)
WHERE cacheobjtype = 'Compiled Plan'
ORDER BY objtype;

  邀月工作室

如果结果类型为Prepared,它意味着SQL Server没有自动参数化你的查询。这种情况下,你可以试着修改数据库为Parameterization Forced ,但这个选项影响整个数据库。其中有些查询并不会因此受益。为了强制SQL Server仅对某些特定固定的查询,计划向导派上用场了。下文将关注。

记住,缓存被按每批处理级而定。如果你试着使用sp_executesql或Prepare和execute强制参数化,所有批处理中的语句必定被为可重用的计划而参数化。如果一个批处理有一些参数语句,一些常量,每一个不同的常量的批处理的执行被看作是distinct,批处理的部分参数化没有值。

其他缓存冲突

除了查看可以显示缓存问题的等待类型,还有其他的编码行为显示计划重用的副作用:

检验参数类型 预编译查询和自动参数化 对于已编译的查询,实际上已经指定了参数数据类型。但当SQL Server参数化时,它自己决定参数的数据类型。这个在前文已有描述。如果传递的参数是数字,SQL Server基于精度和刻度决定数据类型。8.4对应numeric(2,1),而8.44对应numeric(3,2)。对于varchar,服务端参数不依赖于实际值的长度。我们看两个查询:

SELECT * FROM Customers
WHERE CompanyName = 'Around the Horn';
GO
SELECT * FROM Customers
WHERE CompanyName = 'Rattlesnake Canyon Grocery';
GO

这两个查询的参数化如下:

 

(@1 varchar(8000))SELECT * FROM [Customers] WHERE [CompanyName]=@1

监视计划缓存和数据缓存大小 通常来说,当更多的查询运行时,数据缓存页所使用的内存数量会跟随计划缓存的内存数量而增长。然而,在SQL Server 2005 sp2之前,计划缓存的最大限定可能增长到总缓冲池的80%,即达到内存压力限定而强制计划被回收。对那些依赖良好的数据缓存行为的查询,这可能会导致严重的性能下降。一个得到使用计划缓存的数据页和数据缓存页的比较的最容易的地方,是性能计数器。看下列计数器:SQLServer:计划缓存/缓存页(总数)和SQLServer:缓冲管理/数据页

 

处理编译和重编译的问题 有一个侦探过度编译和重编译的工具。你可以使用系统监视器或跟踪器中的一个或事件监视工具。记住编译和重编译不是同一回事。在已经存在的模块或语句被判定为不再有效或不再优化时重编译被执行。所有的重编译都被看成是编译,反之未必。例如,缓存里没有计划,或当执行一个使用Recompile选项执行一个过程或执行一个用Recompile创建的过程时,SQL Server视为编译而非一个重编译。

如果工具显示过度编译或重编译,你可以采用以下动作:

1、如果重编译因为Set选项改变引起,针对T-SQL语句的SQL跟踪文本数据立刻在重编译事件前显示哪一个SET选项被改变。改变SET选项的最佳时机是当第一次连接数据时,而避免在连接并开始提交语句后再改变。或在存储过程内部改变。

2、临时表的重编译阈值比正常表低的多。如果一个临时表的重编译由于统计变换引起,一个跟踪在EventSubClass列有个数据值显示统计因为一个临时表的操作而改变。你可以改变临时表为表变量,后者的统计不会维护。因为没有统计被维护,因此,统计的改变不能削减重编译。然而,统计的缺失导致这些查询的子优化计划减少。你需要考虑表变量的好处是否与它的成本相当。另一个替代方案是使用keep plan查询提示,这将设置临时表的重编译阈值与固定表相同。

3、为了避免所有的重编译由于统计改变,或是固定表或临时表而引起,你可以定义keepfixed plan查询提示。有了这个提示(hint),重编译将只因为纠错相关的(correctness-related)原因,例如,当一个被语句费用的表的架构改变或一个表被使用sp_compile存储过程标记为重编译时,一个重编译发生。

4、另一个阻止由于统计改变而引起重编译的方法是关闭(索引和列)统计的自动更新。请慎用这个关闭开关。如果关闭,查询优化器将不再对数据变化敏感,可能接下来会影响子优化计划。这可以看作其他方案的最末一个。

5、所有的T-SQL代码应该使用两部分名称,以明确什么对象被引用,这将避免重编译。

6、不要在条件结构如IF语句中使用DDL

7、检查存储过程是否被with Recompile选项创建。大多数情况下,存储过程中仅有一两个语句会使整个存储过程从每一个执行的重编译中受益。我们可以仅仅为这些语句使用recompile查询提示。这比对整个存储过程使用with recompile选项要好的多,后者会在每次存储过程执行时每个语句都被重编译。

下文将关注计划向导和优化提示。
  

posted @ 2010-07-15 23:22  邀月  阅读(1652)  评论(0编辑  收藏  举报