【问题标题】:Why is my stored proc that uses temp tables slower than the SQL in a batch outside of a stored procedure?为什么我使用临时表的存储过程比存储过程之外的批处理中的 SQL 慢?
【发布时间】:2021-12-31 20:01:41
【问题描述】:

我有一些 SQL 可以自己快速运行,但是当同一个 SQL 在存储过程中时速度会慢 60 倍。 SQL 在其许多查询中使用临时表。为什么在 sp 中速度较慢?

【问题讨论】:

    标签: performance stored-procedures sybase temp-tables sap-ase


    【解决方案1】:

    tl;dr

    填充(临时)表后,考虑 a) 运行 create index 和/或 b) 在桌子上运行update statistics;这两者都提供以下好处:

    • 强制重新编译任何引用 (temp) 表的后续查询
    • 为编译器提供有关 (temp) 表 [和索引] 的统计信息

    如果没有minimal, reproducible example(包括 ASE 版本和查询计划),很难 100% 肯定地说,但需要考虑一些想法:

    1. 我猜 自行快速运行的 SQL 是基于 多批次处理(例如,一批创建的临时表,临时表 在一批中填充,查询在另一批中运行)其中 如果查询的编译有利于访问 非空临时表的一些统计信息
    2. 使用 proc 的步骤(创建临时表、填充临时表、运行 query(s)) 都是单个批次的一部分,因此,正如所指出的 在eric's answer 中, 查询将(最初)根据一些假设进行编译 关于临时表大小
    3. 另一种可能性:第一次执行 proc 时,它是基于编译的 在 user1 的(临时表)数据集上; user2 然后运行 ​​proc 并结束 up 使用 user1 的执行计划的缓存版本,但是如果 user2 的(临时表)数据集有很大不同(例如 user2 的数据将生成不同的查询计划)然后应用 user1 对 user2 数据的查询计划可能会导致不良/缓慢 执行

    不管为什么 proc 的查询是使用不太理想的查询计划运行的,一般的“解决方案”是确保使用一些关于 temp 的最新信息来编译查询表。

    如何/何时(重新)编译 proc 的查询取决于 ASE 版本和任意数量的相关场景:

    • 较新的 ASE 版本具有配置参数 deferred name resolutionprocedure deferred compilationoptimize temp table resolution,它们可以指示何时编译 proc 中的单个查询;但如果想法 #2(上图)正在发挥作用,即使这样也可能还不够
    • 可以创建 proc with recompile(在每次运行时强制重新编译)但如果(再次)想法 #2(上文)在起作用,这可能还不够
    • 创建索引(在填充临时表之后)和/或运行 update statistics(同样,最好在填充临时表之后)应该强制重新编译任何后续查询
    • (重新)编译大型复杂查询可能会延长查询的整体运行时间(与重新使用预先存在的查询计划相反);最终结果是过度重新编译会导致整体性能下降
    • 使用抽象查询计划(以及其他优化器提示)虽然不能消除重新编译,但可以帮助减少(重新)编译查询的时间 [这开始进入高级 P&T工作 - 可能比这个特定问答所需的更多]

    虽然文档建议在调用 proc 之前创建临时表可能有一些好处,但这里也存在一些缺陷:

    • 必须管理/记住父进程(创建临时表的批处理或进程)与子进程(运行有问题的查询)之间的关系
    • 对于由多个/不同父进程调用的子进程,所有父进程都必须使用相同的临时表 DDL,否则子进程可能会由于临时表中的架构更改而导致过度重新编译桌子;问题是,如果 proc 注意到不同的临时表结构(例如,不同的数据类型、以不同顺序命名的列、不同的索引),那么它将强制重新编译

    【讨论】:

      【解决方案2】:

      Sybase says, "当您在使用它的同一个存储过程或批处理中创建表时,查询优化器无法确定该表的大小,因为该表不是在优化查询时创建的。这适用于临时表和普通用户表。”

      Sybase 建议在存储过程之外创建临时表。如果您以某些方式创建索引,还有一些解决方案。请参阅Sybase docs 了解更多详情

      【讨论】:

      • 能否要求 sybase 在 SP 中“重新编译”语句?
      • 不在 sp。 sp 可以设置为每次运行时重新编译。这对我没有帮助,但对其他人有帮助。
      【解决方案3】:

      你的存储过程有参数吗?

      如果是,SQL Server 在执行具有参数的存储过程时会使用一个称为参数嗅探的过程。当过程被编译或重新编译时,传递给参数的值被评估并用于创建执行计划。然后将该值与执行计划一起存储在计划缓存中。在随后的执行中,使用相同的值和相同的计划。

      当您查询的表中的值分布不均匀时会发生什么?如果一个值返回 10 行而另一个值返回 10,000 行或 1000 万行会怎样? 将发生的情况是,第一次运行过程并编译计划时,传入的任何值都与计划一起存储。每次执行时,直到重新编译,都将使用相同的值和计划——不管它是该值的最快计划还是最佳计划。

      您可以强制 SQL Server 在每次运行时重新编译存储过程。这样做的好处是每次运行时都会创建最佳查询计划。但是,重新编译是 CPU 密集型操作。对于频繁运行的存储过程,或者在已经受 CPU 资源限制的服务器上,这可能不是一个理想的解决方案。

      ALTER PROCEDURE SP_Test
      @ProductID INT
      WITH RECOMPILE
      AS
       
      SELECT OrderID, OrderQty
      FROM SalesOrderDetail
      WHERE ProductID = @ProductID
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2012-11-19
        • 2015-11-20
        • 2014-12-22
        • 2011-08-26
        • 1970-01-01
        • 2015-02-15
        相关资源
        最近更新 更多