如何解决Oracle SQL语句的性能问题

时间:2008-09-19 17:51:09

标签: database performance oracle

我有两个插入语句,几乎完全相同,它们在同一个Oracle实例上以两个不同的模式运行。插入语句看起来并不重要 - 我在这里寻找故障排除策略。

两种模式都有99%相同的结构。一些列的名称略有不同,除了它们是相同的。 insert语句几乎完全相同。一个解释计划的成本为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万!),以及每次操作花费的时间(与其他操作相比)。

9 个答案:

答案 0 :(得分:4)

答案 1 :(得分:2)

插入减速的主要元凶是索引,约束和oninsert触发器。做一个没有尽可能多的测试,你可以删除,看看它是否很快。然后重新介绍它们,看看哪一个导致问题。

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

答案 2 :(得分:1)

首先要意识到的是,作为the documentation says,您看到的成本是相对于其中一个查询计划的。 2种不同解释的成本可比较。其次,成本基于内部估计。与Oracle一样努力,这些估计并不准确。特别是当优化器行为不当时。您的情况表明,根据Oracle,有两个查询计划在性能上非常接近。但实际上,它的表现却截然不同。

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

下一步是什么?那么有很多策略来调整坏的陈述。如果您使用的是Oracle 10g,我建议的第一个选项是尝试使用SQL tuning advisor来查看更详细的分析是否会告诉Oracle错误的方法。然后它可以存储该计划,您将使用更有效的计划。

如果你不能这样做,或者如果那不起作用,那么你需要进入诸如提供查询提示,手动存储的查询轮廓等之类的事情。这是一个复杂的话题。这是真正的DBA有用的地方。如果你不这样做,那么你会想要开始阅读documentation,但要注意有很多东西需要学习。 (Oracle也有一个非常好的SQL调优类。但它并不便宜。)

答案 3 :(得分:1)

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

Favourite performance tuning tricks

......作为核对表可能会有所帮助,即使它不是特定于Oracle的。

答案 4 :(得分:0)

我同意之前的海报,SQL Trace和tkprof是一个很好的起点。我还强烈推荐书籍Optimizing Oracle Performance,该书讨论了跟踪执行和分析输出的类似工具。

答案 5 :(得分:0)

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

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

答案 6 :(得分:0)

另一个提供查询调优通用技术的好参考是Dan Tow的书SQL Tuning

答案 7 :(得分:0)

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

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

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

有两个要比较的陈述,你可以明显地比较两个执行计划,看看它们的不同之处。

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

接下来要找出一种方法让Oracle使用该执行计划。通常使用Hints或创建附加索引,有时会更改SQL语句。然后当然测试改变后的陈述

a)仍然做它应该做的事情

b)实际上更快

使用b确保测试正确的用例非常重要。典型的陷阱是返回第一行与返回最后一行之间的差异。大多数工具会在可用时立即显示第一批结果,没有直接指示,还有更多工作要做。但是如果你的实际程序在继续下一个处理步骤之前必须处理所有行,那么当第一行显示时几乎无关紧要,只有在最后一行可用时它才有意义。

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

答案 8 :(得分:-1)

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