临时表和常量语句重新编译

时间:2018-02-03 11:36:33

标签: sql-server sql-server-2016 stress-testing

我正在对在动态SQL中使用临时表的系统进行压力测试。该表在事务的早期创建,并由若干存储过程中的若干动态SQL语句填充,这些存储过程作为批处理的一部分使用以下形式的语句执行:

INSERT #MyTable (...)
SELECT ...

SELECT语句相当复杂,因为它可能包含UNION ALLUNPIVOT语句,并且引用了几个UDF。所有字符串都使用sp_executesql执行,并启用参数嗅探。

我注意到在负载下我看到很多RESOURCE_SEMAPHORE_QUERY_COMPILE等待重新编译的查询文本存在且同时在几个等待中相同并出现在持续的压力测试中5分钟。服务器上的内存消耗通常约为60%利用率,SQL Server可以消耗的数量没有限制。限制因素似乎是CPU,在测试期间始终保持在> 95%。

我在测试期间对服务器进行了分析,以观察SQL:StmtRecompile事件,该事件突出显示重新编译的原因:

  

5 - 临时表已更改

但每次临时表都是相同的,并且除了在批处理结束时删除表时,除了在表中删除之外,没有对表执行DDL语句。

到目前为止,我已经尝试过:

  • 启用“针对临时工作负载进行优化”选项
  • OPTION(KEEPFIXED PLAN)
  • 将动态语句更改为SELECT,然后使用INSERT ... EXEC,以便临时表不在执行的字符串中

所有这些都没有任何区别,等待仍然存在。

为什么SQL认为每次执行时都需要重新编译这些相同的查询,如何让它保留并重用它正在创建的缓存计划?

注意:我无法将临时表更改为In-Memory表,因为有时使用此表的存储过程可能必须在同一实例上查询另一个数据库。

这是使用SQL Server 2016 SP1 CU7。

1 个答案:

答案 0 :(得分:0)

在动态SQL字符串中删除插入临时表似乎可以显着提高性能。例如,更改此内容:

EXEC sp_executesql
    N'INSERT #tempTable (...) SELECT ... FROM ...'

其中SELECT语句非常重要,对此:

INSERT #tempTable (...)
EXEC sp_executesql
    N'SELECT ... FROM ...'

大大减少了编译期间创建的块数。不幸的是,不能避免重新编译查询,只是重新编译的查询现在更加简单,因此CPU密集度更低。

我还发现使用与临时表相同的列创建内存表类型更高效,将复杂插入执行到该类型的表变量并执行从表变量到temp的单个插入桌子最后。