如果您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]
答案 0 :(得分:3)
除了refcounts和usecounts列
之外,其他所有内容都是相同的
事实并非如此。您的计划有所不同,您可以看到pagesused
和setopts
列。
第一个是缓存对象消耗的页数,因此您可以清楚地看到一个计划更大。第二列(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_level
和date_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%';
以下是我所做的事情: