为什么SQL Server对同一个查询有多个缓存计划?

时间:2017-12-27 00:21:29

标签: sql-server

如果您select * from syscacheobjects,您可以看到有关数据库查询的一些简单统计信息。我喜欢用这个表来查找要调整的查询。我特别关注usecounts = 1的查询。有时我会看到同一个查询的两行,这让我很困惑。

注意:查询已参数化。

考虑一下:

| bucketid | cacheobjtype  | objtype  | objid     | dbid | dbidexec | uid | refcounts | usecounts | pagesused | setopts | langid | dateformat | status | lasttime | maxexectime | avgexectime | lastreads | lastwrites | sqlbytes | sql         |
|----------|---------------|----------|-----------|------|----------|-----|-----------|-----------|-----------|---------|--------|------------|--------|----------|-------------|-------------|-----------|------------|----------|-------------|
| 7650     | Compiled Plan | Prepared | 482910160 | 5    | 0        | -2  | 2         | 724       | 21        | 251     | 0      | 1          | 0      | 0        | 0           | 0           | 0         | 0          | 1918     | SELECT blah |
| 7650     | Compiled Plan | Prepared | 482910160 | 5    | 0        | -2  | 7         | 705505    | 81        | 251     | 0      | 1          | 0      | 0        | 0           | 0           | 0         | 0          | 1918     | SELECT blah |

除refcounts和usecounts列外,其他所有内容都相同。

那为什么会有两排?

更新

一个人也从新的管理模式获得相同的多于一行的情况。该引擎确实创建了不止一次的缓存计划。谁知道为什么?

SELECT *
FROM 
    sys.dm_exec_cached_plans PLANS
    CROSS APPLY sys.dm_exec_sql_text (PLANS.plan_handle) AS Q
where [text] like '%SOMETHING%'
order by [text]

vs

select * 
from syscacheobjects 
where sql like '%SOMETHING%'
order by [sql]

1 个答案:

答案 0 :(得分:3)

  

除了refcounts和usecounts列

之外,其他所有内容都是相同的

事实并非如此。您的计划有所不同,您可以看到pagesusedsetopts列。

第一个是缓存对象消耗的页数,因此您可以清楚地看到一个计划更大。第二列(setopts)是关键列,这是为同一查询提供两个不同计划的原因。以下是本专栏的BOL说明:

  

设置影响已编译计划的选项设置。这些设置是   缓存键的一部分。此列中值的更改表示用户   修改了SET选项。

如您所见,两个不同的会话选项集合有两个计划,21和81。

21表示这些选项已开启:

ANSI_PADDING

ANSI_WARNINGS

FORCEPLAN

81表示这些选项已开启:

ANSI_PADDING

ANSI_WARNINGS

QUOTED_IDENTFIER
  

当FORCEPLAN设置为ON时,SQL Server查询优化器进程   一个连接的顺序与表中显示的一个表的FROM子句相同   查询。此外,将FORCEPLAN设置为ON会强制使用嵌套   循环连接,除非构建计划需要其他类型的连接   对于查询,或者使用连接提示或查询提示请求它们。

所以你看到你的计划不同,因为其中一个有FORCED ORDER和其他没有。

Set options成为缓存键的一部分,这意味着如果您有两个具有不同设置选项的会话,则将生成2个计划(相同或不同)。当另一个会话尝试执行相同的查询时,其设置选项定义将使用的计划,如果这些选项与已存在于缓存中的选项不同,则将为此查询和第三组会话创建和缓存第三个计划选项。

您可以在此处详细了解:Slow in the Application, Fast in SSMS? by Erland Sommarskog

<强>更新

答案仍然在上面引用的文章中。

  

如果同一过程的缓存中有多个条目,则条目在缓存中至少有一个区别   键即可。缓存键是运行时设置,由于某种原因或   另一个要求不同的查询计划。大多数这些设置是   用SET命令控制,但不是全部。

...

  

一个非常重要的缓存   键是set_options。这是一个用于设置a的掩码   可以打开或关闭的SET选项数。如果你进一步观察   sys.dm_exec_plan_attributes的主题,你找到一个列表   详细说明每个位描述的SET选项。 (你也会看到   还有一些不受SET控制的项目   命令。)因此,如果两个连接设置了这些选项中的任何一个   不同的是,连接将使用不同的缓存条目   相同的程序 - 因此他们可能使用不同的查询   计划,可能在性能上有很大差异。

首先我认为区别在setopts,但它们是相同的。 然后,我了解到setopts 并未反映所有设置选项

例如,syscacheobjects确实包含dateformat列,但它也是一个设置选项!

所以我只想到 setopts 中未反映的内容,但仍然是cache key。例如,这些是compatibility_leveldate_first

我决定改变date_first,我能够重现你的情况。

我创建了预备语句(您的计划是针对prepared),只是将date_first从1更改为7,然后我检查了sys.syscacheobjects,我找到了2个计划,其中所有列都相同在你的情况下,但你仍然可以使用Erland Sommarskog的文章中的代码找到你的案例中cache key的不同之处:

SELECT qs.plan_handle, a.attrlist
FROM   sys.dm_exec_query_stats qs
CROSS  APPLY sys.dm_exec_sql_text(qs.sql_handle) est
CROSS  APPLY (SELECT epa.attribute + '=' + convert(nvarchar(127), epa.value) + '   '
              FROM   sys.dm_exec_plan_attributes(qs.plan_handle) epa
              WHERE  epa.is_cache_key = 1
              ORDER  BY epa.attribute
              FOR    XML PATH('')) AS a(attrlist)
WHERE est.text like '%2343513B-14B6-4BF3-A262-48F645A35A39%'
  and est.text not like '%syscacheobjects%';

感兴趣的列是attrlist,您可以在其中找到所有cache key,这样您就可以找到产生不同计划的2个会话中的不同内容。

这是完整的复制品:

(我使用newid()函数来获取我在查询中加盖的GUID,以便以后在计划缓存中轻松找到它)

--set datefirst 1; -- execute the code as it is then uncomment this and comment the next row
set datefirst 7; 

declare @sql nvarchar(4000) =
N'select * /*2343513B-14B6-4BF3-A262-48F645A35A39*/
from s1057.dbo.Nums where n = @n';

declare @p nvarchar(100) = '@n int';

exec sp_executesql @sql, @p, 10;

然后使用此代码查找具有相同setopts但不同cache key的2个生成的计划:

select * 
from sys.syscacheobjects 
where sql like '%2343513B-14B6-4BF3-A262-48F645A35A39%'
  and sql not like '%syscacheobjects%';


SELECT qs.plan_handle, a.attrlist
FROM   sys.dm_exec_query_stats qs
CROSS  APPLY sys.dm_exec_sql_text(qs.sql_handle) est
CROSS  APPLY (SELECT epa.attribute + '=' + convert(nvarchar(127), epa.value) + '   '
              FROM   sys.dm_exec_plan_attributes(qs.plan_handle) epa
              WHERE  epa.is_cache_key = 1
              ORDER  BY epa.attribute
              FOR    XML PATH('')) AS a(attrlist)
WHERE est.text like '%2343513B-14B6-4BF3-A262-48F645A35A39%'
  and est.text not like '%syscacheobjects%';

以下是我所做的事情:

enter image description here