计划缓存和复制
一旦优化器产生计划,sql server就会尽力确保再次利用它。要做到这一点,就要把计划缓存起来,并采取措施确保计划尽可能被广泛地复用,而这又是通过使用参数化选项实现的。sql server借助参数化实现类似查询复用同一计划。参数化的查询可以通过sys.dm_exec_cached_plans。参数化由sql server配置选项simple或forced控制。
计划缓存创建在sql os提供的缓存基础设施之上。缓存存储能用于缓存各种对象。计划缓存包含几个不同的缓存存储,用于不同类型的对象。你可以通过下面的sql查看一些缓存存储的内容:
select name, entries_count, pages_kb from sys.dm_os_memory_cache_counterswhere [name] in ( 'object plans', 'sql plans', 'extended stored procedures')
要查找有long lookup times引起的性能问题,你可以查看如下sql,建议bucket包含的对象不超过20个,查过100个就要注意了。
select * from sys.dm_os_memory_cache_hash_tableswhere type in ('cachestore_objcp', 'cachestore_sqlcp', 'cacchestore_phdr', 'cachestore_xproc')
使用下面的sql查看繁重被使用的buckets:
select bucketid, count(*) as entries_in_bucketfrom sys.dm_exec_cached_plansgroup by bucketidorder by 2 desc
另一种查找使用相同查询计划哈希的查询的方式是使用如下t-sql:
select query_plan_hash,count(*) as occurrencesfrom sys.dm_exec_query_statsgroup by query_plan_hashhaving count(*) > 1
计划缓存存放algebrizer树、编译的计划、游标执行上下文、执行上下文等各种对象。下面的sql查看不同类型对象的统计:
select cacheobjtype, objtype, count (*)from sys.dm_exec_cached_plansgroup by cacheobjtype, objtypeorder by cacheobjtype, objtype
要查看执行上下文,你必须传一个plan handle给sys.dm_exec_cached_plans_dependent_objects,然后,做这个之前,你需要运行dbcc freeproccache来清空缓存(不要在生成系统执行)。先运行如下t-sql,查看ad hoc计划的plan_handle:
select p.refcounts, p.usecounts, p.plan_handle, s.textfrom sys.dm_exec_cached_plans as pcross apply sys.dm_exec_sql_text (p.plan_handle) as swhere p.cacheobjtype = 'compiled plan'and p.objtype = 'adhoc'order by p.usecounts desc
将上面结果中的plan_handle带入下面t-sql即可查看执行上下文:
select * from sys.dm_exec_cached_plan_dependent_objects(0x060001006c032129a08d41c00100000001000000000000000000000000000000000000000000000000000000)
带入下面的t-sql可以查看计划的属性:
select * from sys.dm_exec_plan_attributes(0x060001006c032129a08d41c00100000001000000000000000000000000000000000000000000000000000000)
带入下面t-sql可以查看sql text:
select * from sys.dm_exec_sql_text
(0x060001006c032129a08d41c00100000001000000000000000000000000000000000000000000000000000000)
编译和重新编译
编译和重新编译差不多,只是在触发次数上稍有不同。当sql server判定一个计划不再有效,这通常是因为schema变更、统计变更或一些其他的原因,就会重新编译计划。你可以监视编译或重新编译的发生量,通过观察perfmon object sql server: sql statistics,然后查看下面两个计数器:sql compilations/sec and sql recompilations/sec。
影响优化
有两个主要方式能够影响查询优化器--查询提示(hints)或计划向导(guides)。提示要谨慎使用,因为多少情况下sql server已经选择了正确的计划,对于复杂的查询或处理复杂的数据集时,使用提示或许是必要的。使用提示之前,去网上搜索“sql server query hints”,特别是craig fredman写的博客。不像锁定提示(sql server尝试满足),查询提示更强,所以,如果sql server不能满足查询提示,就会产生8622错误,也不会创建任何计划。以下是几个比较受关注的查询提示:
fast :指定对查询进行优化,以便快速检索第一个 number_rows.。该值是非负整数。在返回第一个 number_rows 后,查询继续执行并生成完整的结果集。
{loop | merge | hash } join:指定整个查询中的所有联接操作由 loop join、merge join 或 hash join 执行。如果指定了多个联接提示,则优化器从允许的联接策略中选择开销最少的联接策略。要查看它是如何工作的,可以使用类似下面的语句:
maxdop number:对于指定了max degree of parallelism 配置选项的查询,会覆盖 sp_configure 和资源调控器的该选项。maxdop 查询提示可以超出使用 sp_configure 配置的值。 如果 maxdop 超出使用资源调控器配置的值,则数据库引擎会使用资源调控器 maxdop 值。 当使用 maxdop 查询提示时,所有和 max degree of parallelism 配置选项一起使用的语义规则均适用。 如果 maxdop 设置为零,服务器将选择最大并行度。
optimize for:在编译和优化查询时指示查询优化器对局部变量使用特定值。仅在查询优化期间使用该值,在查询执行期间不使用该值。optimize for unknown指示查询优化器在编译和优化查询时使用所有局部变量的统计数据而不是初始值,包括使用强制参数化创建的参数。
recompile:指示 sql server 数据库引擎在执行为查询生成的计划后将其丢弃,从而在下次执行同一查询时强制查询优化器重新编译查询计划。如果未指定 recompile,数据库引擎将缓存查询计划并重新使用它们。在编译查询计划时,recompile 查询提示将使用查询中任意本地变量的当前值,如果查询位于存储过程中,这些当前值将传递给任意参数。在只须重新编译存储过程中的一部分查询,而不是重新编译整个存储过程时,recompile 是创建使用 with recompile 子句的存储过程的很有用的替代方法。
use plan n'xml_plan':强制查询优化器对查询使用由'xml_plan' 指定的现有查询计划。不能使用 insert、update、merge 或 delete 语句来指定 use plan。
计划向导
在sql server 2005就已出现,它能够让dba在不变更查询本身的情况下影响查询的优化。典型的情况,dba会使用计划向导试图在第三方应用数据库上调校查询执行,在这里执行的t-sql代码是不能被改变的。计划向导在sql server 2008中得到增强。有3种不同类型的计划向导:
对象计划向导:可用于存储过程、触发器或用户自定义函数sql计划向导:用于特定的sql语句模板计划向导:为特定sql查询的参数化提供了覆盖数据库设置的一种途径要利用计划向导,第一步是创建或捕获一个好计划;第二步是把计划应用到你想改变查询优化器行为的对象或t-sql语句。
