【问题标题】:Temporary tables and constant statement recompilation临时表和常量语句重新编译
【发布时间】:2018-02-03 11:36:32
【问题描述】:

我正在对使用动态 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 会认为每次执行这些相同的查询时都需要重新编译它们,我怎样才能让它保留和重用它正在创建的缓存计划?

注意:我不能将临时表更改为内存表,因为有时使用它的存储过程可能必须查询同一实例上的另一个数据库。

这是使用 SQL Server 2016 SP1 CU7。

【问题讨论】:

  • 这个博客显示了你面临的类似问题,上面写着Some other common reasons for recompiles relating to temporary tables include: declare cursor statements whose select statement references a temporary table, or in an exec or sp_executesql statement.

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


【解决方案1】:

看来,删除动态 SQL 字符串中临时表的插入可以显着提高性能。例如,改变这个:

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

SELECT 声明不平凡,对此:

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

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

我还发现创建一个与临时表具有相同列的内存表类型、对该类型的表变量执行复杂插入并从表变量执行单个插入临时表的性能更高表在最后。

【讨论】:

猜你喜欢
  • 2012-02-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-08-20
  • 2012-07-27
  • 1970-01-01
相关资源
最近更新 更多