【问题标题】:How do I troubleshoot performance problems with an Oracle SQL statement如何解决 Oracle SQL 语句的性能问题
【发布时间】:2010-09-11 08:52:24
【问题描述】:

我有两个几乎完全相同的插入语句,它们在同一个 Oracle 实例上的两个不同模式中运行。插入语句是什么样的并不重要——我在这里寻找故障排除策略。

两种模式 99% 的结构相同。一些列的名称略有不同,除了它们是相同的。插入语句几乎完全相同。一个解释计划的成本为 6,另一个解释计划的成本为 7。两组插入语句中涉及的表具有完全相同的索引。两种模式的统计信息都已收集。

一个插入语句在 5 秒内插入 12,000 条记录。

另一个插入语句在 4 分 19 秒内插入 25,000 条记录。

插入的记录数正确。执行时间的巨大差异让我感到困惑。鉴于解释计划中没有任何突出之处,您将如何确定导致运行时这种差异的原因?

(我在 Windows 机器上使用 Oracle 10.2.0.4)。

编辑:问题最终是一个低效的查询计划,涉及不需要完成的笛卡尔合并。明智地使用索引提示和哈希连接提示解决了这个问题。现在需要 10 秒。 Sql Trace / TKProf 给了我方向,因为它向我展示了计划中的每个步骤花费了多少秒,以及生成了多少行。因此 TKPROF 向我展示了:-

Rows     Row Source Operation
-------  ---------------------------------------------------
  23690  NESTED LOOPS OUTER (cr=3310466 pr=17 pw=0 time=174881374 us)
  23690   NESTED LOOPS  (cr=3310464 pr=17 pw=0 time=174478629 us)
2160900    MERGE JOIN CARTESIAN (cr=102 pr=0 pw=0 time=6491451 us)
   1470     TABLE ACCESS BY INDEX ROWID TBL1 (cr=57 pr=0 pw=0 time=23978 us)
   8820      INDEX RANGE SCAN XIF5TBL1 (cr=16 pr=0 pw=0 time=8859 us)(object id 272041)
2160900     BUFFER SORT (cr=45 pr=0 pw=0 time=4334777 us)
   1470      TABLE ACCESS BY INDEX ROWID TBL1 (cr=45 pr=0 pw=0 time=2956 us)
   8820       INDEX RANGE SCAN XIF5TBL1 (cr=10 pr=0 pw=0 time=8830 us)(object id 272041)
  23690    MAT_VIEW ACCESS BY INDEX ROWID TBL2 (cr=3310362 pr=17 pw=0 time=235116546 us)
  96565     INDEX RANGE SCAN XPK_TBL2 (cr=3219374 pr=3 pw=0 time=217869652 us)(object id 272084)
      0   TABLE ACCESS BY INDEX ROWID TBL3 (cr=2 pr=0 pw=0 time=293390 us)
      0    INDEX RANGE SCAN XIF1TBL3 (cr=2 pr=0 pw=0 time=180345 us)(object id 271983)

注意操作是 MERGE JOIN CARTESIAN 和 BUFFER SORT 的行。促使我关注这一点的是生成的行数(超过 200 万!),以及每个操作花费的时间(与其他操作相比)。

【问题讨论】:

  • 一朵云和一个西瓜有 99% 相同——水……并不意味着它们是等价的。我可以有两个针对页面的巨大查询并将 = 更改为 != 并且可以完全改变性能。
  • 标记 - 插入语句位于两个不同的模式中,具有完全相同的表,有一些列的名称不同。数据类型相同。插入语句使用相同的表、相同的连接策略等。

标签: database performance oracle


【解决方案1】:

【讨论】:

  • 有用 - 似乎是一个很好的通用额外故障排除工具,这就是我想要这个问题的内容。正如我在其他地方就这个问题所评论的那样,我们目前正在研究表空间,但如果没有结果,我会使用这个工具。
  • 这是要走的路,TRACE + TKPROF 会准确地告诉你你的会话在做什么以及它花费的时间在哪里。
  • 表空间?我从来没有通过检查表空间发现查询问题。
  • 链接已损坏。谁有新链接?
【解决方案2】:

插入速度变慢的主要原因是索引、约束和 oninsert 触发器。在没有尽可能多的这些的情况下进行测试,看看它是否很快。然后将它们重新引入,看看是哪一个导致了问题。

我见过在批量插入之前删除索引并在最后重建的系统——而且速度更快。

【讨论】:

  • 两个系统具有完全相同的索引、约束和触发器。我们现在正在研究不同的表空间。
  • 删除索引和重建的重点主要是为了避免在加载过程中进行磁盘寻道。作为奖励,您的索引可以保证之后不会碎片化。
【解决方案3】:

首先要意识到的是,作为the documentation says,您看到的显示成本与其中一个查询计划有关。两种不同解释的成本可比。其次,成本基于内部估计。尽管 Oracle 努力尝试,但这些估计并不准确。特别是当优化器行为不端时。您的情况表明有两个查询计划,根据 Oracle 的说法,它们的性能非常接近。但事实上,它们的表现非常不同。

您要查看的实际信息是实际的解释计划本身。这可以准确地告诉您 Oracle 如何执行该查询。它有很多技术性的 gobbeldy-gook,但你真正关心的是知道它从最缩进的部分开始工作,并且在每一步它都根据少数规则之一进行合并。这将告诉您 Oracle 在您的两个实例中的不同之处。

接下来呢?好吧,有多种策略可以调整错误的陈述。如果您使用的是 Oracle 10g,我建议的第一个选项是尝试他们的SQL tuning advisor,看看更详细的分析是否会告诉 Oracle 其方式的错误。然后它可以存储该计划,您将使用更有效的计划。

如果您无法做到这一点,或者这不起作用,那么您需要进行诸如提供查询提示、手动存储的查询大纲等之类的工作。这是一个复杂的话题。这就是拥有真正的 DBA 有帮助的地方。如果你不这样做,那么你会想开始阅读documentation,但要知道有很多东西要学。 (Oracle 也有一个 SQL 调优类,或者至少曾经非常好。不过它并不便宜。)

【讨论】:

    【解决方案4】:

    我同意之前的帖子,即 SQL Trace 和 tkprof 是一个很好的起点。我还强烈推荐这本书 Optimizing Oracle Performance,它讨论了用于跟踪执行和分析输出的类似工具。

    【讨论】:

      【解决方案5】:

      我已经列出了要检查以提高性能作为另一个问题的答案的一般清单:

      Favourite performance tuning tricks

      ...它作为一个清单可能会有所帮助,即使它不是特定于 Oracle 的。

      【讨论】:

        【解决方案6】:

        SQL Trace 和 tkprof 只有在您可以访问这些工具时才有效。我工作的大多数大公司都不允许开发人员访问 Oracle unix ID 下的任何内容。

        我相信您应该能够通过首先了解所提出的问题并阅读每个查询的解释计划来确定问题。很多时候我发现最大的不同是有一些表和索引没有被分析。

        【讨论】:

        • 很公平,但在这种情况下,您可以与 DBA 一起为您完成这项工作吗? FWIW,我确保架构中的所有表/索引都有统计信息,并且所有列都有直方图。我也阅读了解释计划,但无法缩小范围。
        【解决方案7】:

        另一个介绍查询调优通用技术的好参考书是 Dan Tow 的《SQL Tuning》一书。

        【讨论】:

          【解决方案8】:

          分析 oI 还强烈推荐《优化 Oracle 性能》一书,其中讨论了用于跟踪执行和输出的类似工具。

          【讨论】:

            【解决方案9】:

            当 sql 语句的性能不如预期/期望时,我要做的第一件事就是检查执行计划。

            诀窍是检查与预期不符的事情。例如,您可能会发现您认为索引扫描应该更快的表​​扫描,反之亦然。

            oracle 优化器有时会出错的一个点是估计一个步骤将返回多少行。如果执行计划需要 2 行,但您知道它将更像 2000 行,那么执行计划必然不是最优的。

            通过两个语句进行比较,您显然可以比较两个执行计划以了解它们的不同之处。

            从这个分析中,我提出了一个我认为应该更适合的执行计划。这不是一个确切的执行计划,而只是我发现的一些关键更改,例如:它应该使用 Index X 或 Hash Join 而不是嵌套循环。

            接下来的事情是想办法让 Oracle 使用该执行计划。通常通过使用提示,或创建附加索引,有时更改 SQL 语句。然后当然测试更改后的语句

            a) 仍然做它应该做的事

            b) 实际上更快

            对于 b,确保您正在测试正确的用例非常重要。典型的坑洼是返回第一行与返回最后一行之间的差异。大多数工具一旦可用就会立即向您显示第一个结果,没有直接指示,表明还有更多工作要做。但是如果您的实际程序必须在继续下一个处理步骤之前处理所有行,那么第一行出现时几乎无关紧要,只有当最后一行可用时才相关。

            如果你找到更好的执行计划,最后一步就是让你的数据库在实际程序中实际使用它。如果你添加了一个索引,这通常会开箱即用。提示是一种选择,但如果库创建您的 sql 语句可能会出现问题,这些通常不支持提示。作为最后的手段,您可以保存和修复特定 sql 语句的执行计划。我会避免这种方法,因为它很容易被遗忘,并且在一年左右的时间里,一些可怜的开发人员会摸不着头脑,为什么该语句的执行方式可能与一年前的数据相符,但不适用于当前数据...

            【讨论】:

              猜你喜欢
              • 2011-07-01
              • 1970-01-01
              • 2022-12-07
              • 2021-12-20
              • 2013-05-11
              • 2021-08-29
              • 2020-08-30
              • 1970-01-01
              • 2021-08-09
              相关资源
              最近更新 更多