《Microsoft Sql server 2008 Internals》读书笔记订阅地址:
http://www.cnblogs.com/downmoon/category/230397.html/rss
《Microsoft Sql server 2008 Internals》索引目录:
《Microsoft Sql server 2008 Internal》读书笔记--目录索引
上文我们了解编译对象和重编译的起因之一:Correctness-Based Recompiles(基于更正的重编译),现在我们继续了解重编译的另一个起因:Optimality-based Recompiles(基于优化的重编译)
■Optimality-based Recompiles (基于优化的重编译)
如果SQL Server有理由猜证已经存在的计划已经不再优化,也可能选择重编译一个计划。猜证一个非优化的计划的首要原因是应付基础数据(underlying data)的变化。如果生成查询计划的任何一个统计量自计划被创建时被更新,或者任何一个统计量被认为是Stale(过期的),SQL Server重新编译查询计划。
1、已更新的统计量
统计量可能手动或自动被更新。手动更新在有人运行sp_updatestats或update statitics命令时发生。自动更新在SQL Server决定已经存在的统计量是过时的(out of date)或过期的(stale)。但这些更新只有在数据库选项AUTO_UPDATE_STATISTICS或AUTO_UPDATE_STATISTICS_ASYNC为ON时才发生。如果另一个批处理试图使用当前计划的表或视图,SQL Server侦探到统计已经过期,于是初始化UPDATE STATISTICS操作。
2、过期的统计量
SQL Server在第一次编译(在缓存中没有查询计划的)批处理时侦探过期的统计量,也侦探在已有计划的过期统计量。下图展示了查找一个已存在的计划并检查是否需要重编译时调用步骤的流程图:
如果数据库的AUTO_UPDATE_STATISTICS_ASYNC选项为ON,则SQL Server不必等待统计量的更新完成,它直接基于过期的统计重编译。
如果足够数量的修改发生在支持统计的列时,统计会被认为是过期的(stale)。每个表有一个重编译阈(Recompile threshold,RT),用来决定多少变化发生时该表的任何统计被标志为stale。针对批处理中的所有表的该RT值,被存储在批处理的查询计划中。
该RT值依赖于表的类型,即持久表还是临时表,还有本次计划被编译时表的行数。精准的决定RT值的算法随SQL Server的每个Service Package而变化。以下只针对于SQL Server 2008的RTM版:
1、无论持久表还是临时表,如果N<=500,RT值为500,即对一个小表,你必须做500个变化才能触发重编译。对于大表,必须至少500个变化,再加上行数的20%.
2、 对于临时表,算法是相同的,只有一个例外,如果表非常小或空表(N<6个任何数据修改运算)我们所需要的是6个变化才能触发一个重编译。这意味着一个创建临时表的过程,插入6行到表后,只要临时表被访问,表将不得不重编译。
小技巧:你可以使用KEEP PLAN查询提示(Query Hint)来创建临时表,以避开频繁重编译的批处理。可以使用下列语句对比查看对系统性能的改进:
SELECT <column List> from dbo.PermTable A Inner join #TempTalbe B On B.col1=A.col2 where <filter condition> Option <keep plan>
3、 对于表变量,没有RT值。也就是说,在表变量中不会因为行数的变化而引起重编译。
■ 修改计数器
RT值其实就是SQL Server识别统计是否过期所需要的变化数量。在SQL Server 2005以前的版本中,sysindexes系统表中的列rowmodctr保持跟踪实际发生的变化数量。这个计数器跟踪表或索引的任何行的变化,即便是一个没有被任何索引或有用的统计使用的列发生变化。而SQL Server 2008使用一组列修改计数器或colmodctr。这些计数器是非事务的,即如果事务开始,插入数千行到表中,此是事务回滚,修改计数器的变化却不回滚。不像sysindexes中的rowmodctr,colmodctr值用户不可见,它只能被查询优化器使用。
■使用colmodctr值跟踪表和索引视图的变化
下表给出影响内部colmodctr值变化的因素:
语句 | colmodctr值的变化 |
insert | 每个行被插入后所有colmodctr值加1 |
delete | 每个行被插入后所有colmodctr值加1 |
update | 如果更新到一个非键值列,每行被更新时所有colmodctr值加1 如果更新到一个键值列,对表中的每个列,每行被更新时所有colmodctr值加2 |
bluk insert | 看作N insert操作。N行被插入时,所有colmodctr增加N |
truncate table | 看作N delete操作。N行被插入时,所有colmodctr增加N |
■跳过重编译步骤
以下几种情况下,SQL Server会因为优化考虑跳过编译一个计划:
1、 当一个计划是一个微小的(trivial)计划。一个trivial计划是基于查询用到的表,和这些表上的索引(或缺少索引),却没有替代计划。此时,只有一种方式处理查询,无论是否统计发生改变,任何编译都是浪费资源。
2、如果一个查询包含是选项提示Keepfixed plan,SQL Server将不会由于优化相关的原因而重编译计划。
3、如果针对索引的统计自动更新,并且定义在表或索引视图上的统计被禁用,那么所有这些索引和统计引起的优化相关的重编译的计划将停止。
注意:关闭自动更新内容通常不是一个好的选择,因为这样查询优化器将不再对对象中的数据变化敏感,而子优化查询将不再轻易地得到结果。你可以考虑使用这个技巧,仅仅在最后用完所有其他替代方法以避免重编译时作为一个求助(resort)。
4、如果查询中引用的表是只读的,SQL Server将不重新编译计划。
在SQL Server 2000中,当一个批处理被重编译时,批处理中的所有语句都将被重编译,并不限于初始化重编译的那个。SQL Server提供语句级别的重编译。这意味着SQL Server在重编译过程中使用更小的CPU时间和内存。
下文将关注如何从缓存中移除计划和计划缓存内部操作。