Fre*_*gen 10 performance sql-server sql-server-2016 query-performance
我们有一个第三方应用程序,可以批量发送 T-SQL 语句。
该数据库托管在 SQL Server 2016 Enterprise SP1 CU7、16 核和 256GB 内存上。已启用 Ad-Hoc 优化。
这是正在执行的查询的虚拟示例:
exec sp_executesql N'
IF @@TRANCOUNT = 0 SET TRANSACTION ISOLATION LEVEL SNAPSHOT
select field1, field2 from table1 where field1=@1
option(keep plan, keepfixed, loop join)
select field3, field4 from table2 where field3=@1
option(keep plan, keepfixed, loop join)', N'@1 nvarchar(6)',@1=N'test'
Run Code Online (Sandbox Code Playgroud)
当我监视数据库并查看批处理/秒和编译/秒时,我注意到它们总是相同的。在重负载下,这可以是 1000 批/秒和 1000 次编译/秒。在平均负载下,有 150 个批次/秒。
我分析了最近编译计划的查询缓存:
SELECT TOP (1000) qs.creation_time
, DatabaseName = DB_NAME(st.dbid)
, qs.execution_count
, st.text
, qs.plan_handle
, qs.sql_handle
, qs.query_hash
FROM sys.dm_exec_query_stats qs
CROSS APPLY sys.dm_exec_sql_text(qs.plan_handle) AS st
ORDER BY creation_time DESC;
Run Code Online (Sandbox Code Playgroud)
当我运行上面的查询时,我只看到 10-20 个新的查询计划/秒。
就像每次sp_executesql调用都会触发编译,但查询计划没有被缓存。
batches/sec 等于 compiles/sec 的原因是什么?
Pau*_*ite 12
就像每次
sp_executesql调用都会触发编译,但查询计划没有被缓存。
SQL Server不会缓存仅包含sp_executesql调用的批处理的查询计划。如果没有缓存计划,每次都会进行编译。这是设计使然,也是意料之中的。
SQL Server 避免缓存批处理,编译成本低。这些年来,缓存和不缓存的细节已经改变了很多次。有关详细信息,请参阅我对跟踪标志 2861 的回答以及“零成本”计划的实际含义。
总之,复用的概率(包括具体的参数值)很小,编译包含sp_executesql调用的 ad hoc 文本的成本很小。由 产生的内部参数化批次sp_executesql当然会被缓存和重用 - 这就是它的价值。扩展存储过程sp_executesql本身也被缓存。
要缓存和重用,该sp_executesql语句必须是被认为值得缓存的更大批次的一部分。例如:
-- Show compilation counter
SELECT
DOPC.[object_name],
DOPC.cntr_value
FROM sys.dm_os_performance_counters AS DOPC
WHERE
DOPC.counter_name = N'SQL Compilations/sec'
GO
-- This is only here to make the batch worth caching
DECLARE @TC integer =
(
SELECT TOP (1) @@TRANCOUNT
FROM master.dbo.spt_values AS SV
);
-- Example call we are testing
-- (use anything for the inner query, this example uses the Stack Overflow database
EXECUTE sys.sp_executesql
N'SELECT LT.Type FROM dbo.LinkTypes AS LT WHERE LT.Id = @id;',
N'@id int',
@id = 1;
GO
-- Show compilation counter again
SELECT
DOPC.[object_name],
DOPC.cntr_value
FROM sys.dm_os_performance_counters AS DOPC
WHERE
DOPC.counter_name = N'SQL Compilations/sec'
Run Code Online (Sandbox Code Playgroud)
多次运行该代码。然而,在第一次,许多汇编按预期报告。第二次,不报告编译,除非optimize for ad hoc workloads启用(因此只缓存编译计划存根)。第三次,在任何情况下都不会报告编译,因为任何存根都被提升为完全缓存的临时计划。
删除该DECLARE @TC语句以查看该sys.sp_executesql语句在没有它的情况下永远不会被缓存,无论它被执行多少次。
查看相关的计划缓存条目:
-- Show cached plans
SELECT
DECP.refcounts,
DECP.usecounts,
DECP.size_in_bytes,
DECP.cacheobjtype,
DECP.objtype,
DECP.plan_handle,
DECP.parent_plan_handle,
DEST.[text]
FROM sys.dm_exec_cached_plans AS DECP
CROSS APPLY sys.dm_exec_sql_text(DECP.plan_handle) AS DEST
WHERE
DEST.[text] LIKE N'%sp_executesql%'
AND DEST.[text] NOT LIKE N'%dm_exec_cached_plans%';
Run Code Online (Sandbox Code Playgroud)
相关问答:触发器每次都编译吗?
Han*_*non 11
您可以近似在 Performance Monitor 和 Activity Monitor 中看到的SQL Compilations/sec和Batch Requests/sec,同时在单独的查询窗口中运行一些批处理作为测试,如下详述。
查询窗口1:
DECLARE @t1 datetime;
DECLARE @t2 datetime;
DECLARE @CompVal1 int;
DECLARE @CompVal2 int;
DECLARE @ReCompVal1 int;
DECLARE @ReCompVal2 int;
DECLARE @BatchVal1 int;
DECLARE @BatchVal2 int;
DECLARE @ElapsedMS decimal(10,2);
SELECT @t1 = GETDATE()
, @CompVal1 = (
SELECT spi.cntr_value
FROM sys.sysperfinfo spi
WHERE spi.counter_name = 'SQL Compilations/sec '
)
, @ReCompVal1 = (
SELECT spi.cntr_value
FROM sys.sysperfinfo spi
WHERE spi.counter_name = 'SQL Re-Compilations/sec '
)
, @BatchVal1 = (
SELECT spi.cntr_value
FROM sys.sysperfinfo spi
WHERE spi.counter_name = 'Batch Requests/sec '
);
WAITFOR DELAY '00:00:10.000';
SELECT @t2 = GETDATE()
, @CompVal2 = (
SELECT spi.cntr_value
FROM sys.sysperfinfo spi
WHERE spi.counter_name = 'SQL Compilations/sec '
)
, @ReCompVal2 = (
SELECT spi.cntr_value
FROM sys.sysperfinfo spi
WHERE spi.counter_name = 'SQL Re-Compilations/sec '
)
, @BatchVal2 = (
SELECT spi.cntr_value
FROM sys.sysperfinfo spi
WHERE spi.counter_name = 'Batch Requests/sec '
);
SET @ElapsedMS = DATEDIFF(MILLISECOND, @t1, @t2);
SELECT ElapsedTimeMS = @ElapsedMS
, [SQL Compilations/sec] = (@CompVal2 - @CompVal1) / @ElapsedMS * 1000
, [SQL Recompilations/sec] = (@ReCompVal2 - @ReCompVal1) / @ElapsedMS * 1000
, [Batch Requests/sec] = (@BatchVal2 - @BatchVal1) / @ElapsedMS * 1000;
Run Code Online (Sandbox Code Playgroud)
在查询窗口 2 中,在运行上述代码时运行以下命令。代码简单地执行了 100 个 T-SQL 批处理:
EXEC sys.sp_executesql N'SELECT TOP(1) o.name FROM sys.objects o;';
GO 100
Run Code Online (Sandbox Code Playgroud)
如果您切换回查询窗口 1,您将看到如下内容:
?????????????????????????????????????????????????????? ??????????????????????????????????????? ? ElapsedTimeMS ? SQL 编译/秒 ? SQL 重新编译/秒?批处理请求/秒? ?????????????????????????????????????????????????????? ??????????????????????????????????????? ? 10020.00 ? 10.07984031000 ? 0.00000000000 ? 10.07984031000 ? ?????????????????????????????????????????????????????? ???????????????????????????????????????
如果我们看一下这个查询:
SELECT dest.text
, deqs.execution_count
FROM sys.dm_exec_query_stats deqs
CROSS APPLY sys.dm_exec_sql_text(deqs.plan_handle) dest
WHERE dest.text LIKE 'SELECT TOP(1)%'
Run Code Online (Sandbox Code Playgroud)
我们可以确认测试查询执行了 100 次。
在上面的结果,你可以看到我们正在编译每一次的sp_executesql语句执行。这个计划肯定会被缓存,但我们看到了它的编译;是什么赋予了?
在微软的文档说,这一下sp_executesql:
sp_executesql 在批处理、名称范围和数据库上下文方面与 EXECUTE 具有相同的行为。sp_executesql @stmt 参数中的 Transact-SQL 语句或批处理在执行 sp_executesql 语句之前不会被编译。然后@stmt 的内容作为与调用sp_executesql 的批处理的执行计划分开的执行计划进行编译和执行。
因此,sp_executesql 它每次运行时都会被编译,即使命令文本的计划已经在计划缓存中。@PaulWhite 在他的回答中表明,对 sp_executesql 的大多数调用实际上都没有被缓存。
| 归档时间: |
|
| 查看次数: |
422 次 |
| 最近记录: |