我一直注意到我们的查询计划缓存存在一些我认为不寻常的问题,其中缓存中的计划从未超过一天。
通过运行以下查询(由Kimberly Tripp 提供),它表明大多数计划(4.5Gb 的 6Gb 缓存计划或 44813 的 ~50000)是使用计数为 1 的临时查询。
SELECT objtype AS [CacheType]
, count_big(*) AS [Total Plans]
, sum(cast(size_in_bytes as decimal(18,2)))/1024/1024 AS [Total MBs]
, avg(usecounts) AS [Avg Use Count]
, sum(cast((CASE WHEN usecounts = 1 THEN size_in_bytes ELSE 0 END) as decimal(18,2)))/1024/1024 AS [Total MBs - USE Count 1]
, sum(CASE WHEN usecounts = 1 THEN 1 ELSE 0 END) AS [Total Plans - USE Count 1]
FROM sys.dm_exec_cached_plans
GROUP BY objtype
ORDER …Run Code Online (Sandbox Code Playgroud) 阅读有关 Microsoft SQL Server 执行计划缓存的不同解释,我对使用存储过程而不是非动态查询的好处感到困惑。
我所说的非动态查询是指一个完全参数化的查询字符串,它不会通过多次调用而改变。
据我了解:
为存储过程和普通查询都缓存了执行计划。
对于存储过程,执行计划是预先计算好的,这比第一次调用存储过程时的普通查询略有优势。
来源在我看来相当矛盾:
MSDN上的Execution Plan Caching and Reuse 文章对参数化查询和存储过程没有区别。这些小节强调了参数化查询的重要性,以便 SQL Server 可以轻松地缓存执行计划。
SQL Server 查询执行计划 – Basics声称相反(强调我的):
在执行即席查询时,查询计划是基于完整代码创建的,因此不同的参数或代码的任何更改都将阻止重用现有计划。
在 DBA.StackExchange 上,对与存储过程的好处相关的答案的评论表明参数化查询与存储过程具有完全相同的效果。
因此,在执行计划没有从缓存中抛出的情况下,为了实验,我想运行数十亿次相当复杂的查询,该查询将从执行计划中受益,并且需要一个改变的参数每次使用存储过程而不是普通的参数化查询在执行计划缓存方面会有什么好处吗?
¹ 在执行计划范围之外,使用存储过程会带来较小的性能优势,例如在网络占用方面:传递存储过程的名称及其参数略好于传递整个查询。这些好处超出了我的问题范围,这纯粹是关于执行计划缓存。
我知道与使用如下谓词编写的存储过程相关的参数嗅探问题:
CREATE PROCEDURE [dbo].[Get] @Parameter INT = NULL AS BEGIN;
SELECT [Field] FROM [dbo].[Table]
WHERE [Field] = @Parameter
OR @Parameter IS NULL;
END;
Run Code Online (Sandbox Code Playgroud)
根据参数的值,第一次执行时是标量还是 NULL,一个计划被缓存,对于相反的值可能是次优的。
假设 [Field] 是标量,并且是表上的聚簇索引。以下编写存储过程以支持查询的方法的优缺点是什么:
同一个存储过程中的条件选择
CREATE PROCEDURE [dbo].[Get] @Parameter INT = NULL AS BEGIN;
IF(@Parameter IS NOT NULL) BEGIN;
SELECT [Field]
FROM [dbo].[Table]
WHERE [Field] = @Parameter;
END;
ELSE BEGIN;
SELECT [Field]
FROM [dbo].[Table];
END;
END;
Run Code Online (Sandbox Code Playgroud)
存储过程中的动态 SQL
CREATE PROCEDURE [dbo].[Get] @Parameter INT = NULL AS BEGIN;
DECLARE @sql NVARCHAR(MAX) = N'';
SET @sql += N'SELECT …Run Code Online (Sandbox Code Playgroud) 当执行查询时,SQL Server将产生一个查询计划列表,并启发式地选择成本较低的计划。
所选择的计划将存储在计划缓存中,以供后续看到相同查询时使用。
当表的某些属性发生变化或者重建索引时,它会再次产生一个查询计划列表,并启发式地选择一个成本较低的查询计划,并将其存储在计划缓存中。
但是,MSDN 似乎表明估计计划存储在计划缓存中,请参见下面的屏幕截图。那是对的吗?
我知道这个选项的作用以及如何启用它。我的问题是如果我启用它会发生什么事情。
在不提供太多信息的情况下,我们的会计系统是 Microsoft Dynamics 产品,它使用虚拟机、32GB RAM(28GB 可用于 SQL 服务器 [2008 R2])。他们让他们的供应商来查看我们的配置,以解决会计团队一直看到的某些性能问题。他们甚至让另一个 DBA 看看我们的配置。他的建议之一是“查看缺失的索引”。我们可以说,几乎所有存在的 SQL 服务器实例,每个表上都有一个唯一的非聚集索引,这不是我的选择,但我告诉我,对软件访问的表上的索引进行任何更改都可能导致问题小贩。他的第二个目标是启用“针对临时工作负载进行优化”。一世'
通过优化临时工作负载,我知道单次使用计划存储为存根,并且整个计划实际上不会保存在缓存中,直到计划运行两次。有了这样的系统,我们真的会看到任何形式的性能提升吗?根据 Kimberly Tripp 的文章,我运行了以下查询:
SELECT objtype AS [CacheType]
,count_big(*) AS [Total Plans]
,sum(cast(size_in_bytes AS DECIMAL(18, 2))) / 1024 / 1024 AS [Total MBs]
,avg(usecounts) AS [Avg Use Count]
,sum(cast((
CASE
WHEN usecounts = 1
THEN size_in_bytes
ELSE 0
END
) AS DECIMAL(18, 2))) / 1024 / 1024 AS [Total MBs - USE Count 1]
,sum(CASE
WHEN usecounts = 1
THEN 1
ELSE 0
END) AS …Run Code Online (Sandbox Code Playgroud) 据我了解,当您向 Postgres 发送查询时,例如:
SELECT id FROM table1 WHERE name = $1;
Run Code Online (Sandbox Code Playgroud)
Postgres 将创建一个查询计划。该计划将在同一个会话中为同一个查询缓存。但是如果你创建一个函数:
CREATE OR REPLACE FUNCTION get_table1 (parname text) RETURNS bigint
LANGUAGE plpgsql AS
$$BEGIN
RETURN (SELECT id FROM table1 where name = parname);
END$$;
Run Code Online (Sandbox Code Playgroud)
查询计划将为所有未来的会话缓存。
我想通过检查查询分析器的调用频率来验证这个理论。有没有办法检查 Postgres 解析查询的次数?
如果可能,我想监视每秒 #parses 和最小/最大/平均解析持续时间等内容。
我面临一个绝对奇怪的问题,感觉更像是 Postgres 错误而不是算法问题。
我有这个功能:
CREATE FUNCTION sp_connect(mail character varying, passwd character varying, role character varying)
RETURNS json LANGUAGE plpgsql STABLE AS
$$
DECLARE
user_info record;
BEGIN
IF role = 'Role1' THEN
SELECT u.id, r.name INTO user_info
FROM users u
INNER JOIN users_roles ur ON ur.user_id = u.id
INNER JOIN roles r ON ur.role_id = r.id
WHERE u.email = mail
AND u.password = encode(digest(CONCAT(passwd, u.password_salt), 'sha512'), 'hex')
AND r.name = 'Role1';
ELSIF role = 'Role2' THEN
SELECT h.id, 'Role1' AS name …Run Code Online (Sandbox Code Playgroud) 我正在阅读 Grant Friitchey 的 SQL Server 执行计划,他提到:
SQL Server 不会永远将执行计划保存在内存中。使用“年龄”公式将计划的估计成本乘以计划的使用次数,它们会慢慢地从系统中老化出来。lazywriter 进程是一个内部进程,用于释放所有类型的缓存(包括计划缓存),它会定期扫描缓存中的对象并每次将该值减一。
如果满足以下条件,将从内存中删除该计划:
- 系统需要更多内存
- 计划的“年龄”已经到了零
- 该计划当前未被现有连接引用。
他还在书的前面提到了以下内容:
一旦优化器到达一个执行计划,估计的计划就会被创建并存储在一个称为计划缓存的内存空间中——尽管如果一个计划已经存在于缓存中,这一切都是不同的。
如果实际计划和估计计划不同,我会假设该计划理论上可以达到零。即使它存储在缓存中,这也会使估计的计划执行计数为零。
我的问题是计划年龄可以达到零的不同情况是什么?我的假设是否正确?
弗里奇,G.(2012 年)。SQL Server 执行计划。美国斯普林菲尔德:Simple Talk 出版。
正如标题所暗示的那样,我将仅从 sql server 2014/2016 的计划缓存中删除临时查询(未准备好的查询),因为它占据了我主内存的 50% 以上。你有什么建议吗?
非常感谢。
我试图了解 SQL Server 2016 SP3 系统上缓存元数据的一些执行计划,但我无法将我所看到的内容与文档相一致。
文档sys.dm_exec_cached_plans说它包含:
SQL Server 缓存每个查询计划的一行,以加快查询执行速度。
在我正在观察的系统上,该视图现在有 41,283 行。其中绝大多数(37,594 行)是cacheobjtype =“Compiled Plan”和objtype =“Adhoc”。
文档sys.dm_exec_query_stats说它包含:
缓存计划中每个查询语句一行,行的生命周期与计划本身相关。当从缓存中删除计划时,相应的行将从该视图中删除。
我预计此视图中至少有 37,594 行(每个缓存计划一个,如果某些缓存计划有多个语句,则可能更多)。但是,该视图总共有 6,867 行。
这种差异是如此之大,以至于我必须假设我误解了这些视图中应该包含的内容。
sys.dm_exec_query_stats有人可以帮助我理解为什么与 相比 的行数如此之少吗sys.dm_exec_cached_plans?
我尝试在 上将表内部连接在一起plan_handle,唯一的匹配是 1:1 - 换句话说,有数以万计的缓存计划,没有“查询统计”行。
我还认为差异可能是由sys.dm_exec_procedure_stats或中的许多行来解释的sys.dm_exec_trigger_stats,但事实并非如此(分别为 93 行和 2 行)。
对于任何对这个问题的“为什么”感到好奇的人,我试图查看缓存中的各种计划有多旧,并且我不确定除了加入和sys.dm_exec_query_stats检查之外还有什么方法可以做到这一点creation_time。
以下是我用来获取上面引用的数字的查询:
-- total cached plans
SELECT COUNT_BIG(*) AS total_cached_plans
FROM sys.dm_exec_cached_plans decp
-- totals by type
SELECT decp.cacheobjtype, decp.objtype, COUNT_BIG(*) AS plan_count
FROM sys.dm_exec_cached_plans …Run Code Online (Sandbox Code Playgroud)