如何使用SELECT DISTINCT提高Oracle的性能

时间:2009-06-26 22:42:08

标签: performance oracle ofbiz

我目前正在部署基于OFBiz的ERP 正在使用的数据库是Oracle 10g Enterprise

最大问题之一是一些oracle性能问题,分析了ofbiz日志,以下查询:

SELECT DISTINCT ORDER_ID, ORDER_TYPE_ID, ORDER_NAME, EXTERNAL_ID,
 SALES_CHANNEL_ENUM_ID, ORDER_DATE, ENTRY_DATE, VISIT_ID, STATUS_ID, CREATED_BY, 
 FIRST_ATTEMPT_ORDER_ID, CURRENCY_UOM, SYNC_STATUS_ID, BILLING_ACCOUNT_ID, 
 ORIGIN_FACILITY_ID, WEB_SITE_ID, PRODUCT_STORE_ID, TERMINAL_ID, TRANSACTION_ID, 
 AUTO_ORDER_SHOPPING_LIST_ID, NEEDS_INVENTORY_ISSUANCE, IS_RUSH_ORDER, INTERNAL_CODE, 
 REMAINING_SUB_TOTAL, GRAND_TOTAL, LAST_UPDATED_STAMP, LAST_UPDATED_TX_STAMP, CREATED_STAMP, 
CREATED_TX_STAMP, RECIBIR_BODEGAL, RECEPCIONADA_BODEGAL, FECHA_RECEPCION_BODEGAL FROM 
ERP.ORDER_HEADER WHERE ((STATUS_ID = :v0 OR STATUS_ID = :v1 OR STATUS_ID = :v2) AND 
(ORDER_TYPE_ID = :v3)) ORDER BY ORDER_DATE DESC

很慢。我们已经测试了在没有DISTINCT的情况下执行查询,大约需要30秒。表中有4.000.000+个寄存器。 PK字段orderId和几乎所有其他字段都有索引

带有DISTINCT的EXPLAIN PLAN是:

SELECT STATEMENT () (null)
 SORT (ORDER BY)    (null)
  HASH (UNIQUE) (null)
   TABLE ACCESS (FULL)  ORDER_HEADER

没有DISTINCT是:

SELECT STATEMENT () (null)
 SORT (ORDER BY)    (null)
  TABLE ACCESS (FULL)   ORDER_HEADER

关于调优oracle以改善此类查询性能的任何想法? 重写查询非常困难,因为它是由ofbiz自动生成的 所以我认为解决方案是关于调整oracle

提前致谢

编辑:我使用tkprof分析了查询,正如Rob van Wijk和haffax所建议的那样,结果如下

********************************************************************************

SELECT DISTINCT ORDER_ID, ORDER_TYPE_ID, ORDER_NAME, EXTERNAL_ID,
 SALES_CHANNEL_ENUM_ID, ORDER_DATE, ENTRY_DATE, VISIT_ID, STATUS_ID, CREATED_BY, 
 FIRST_ATTEMPT_ORDER_ID, CURRENCY_UOM, SYNC_STATUS_ID, BILLING_ACCOUNT_ID, 
 ORIGIN_FACILITY_ID, WEB_SITE_ID, PRODUCT_STORE_ID, TERMINAL_ID, TRANSACTION_ID, 
 AUTO_ORDER_SHOPPING_LIST_ID, NEEDS_INVENTORY_ISSUANCE, IS_RUSH_ORDER, INTERNAL_CODE, 
 REMAINING_SUB_TOTAL, GRAND_TOTAL, LAST_UPDATED_STAMP, LAST_UPDATED_TX_STAMP, CREATED_STAMP, 
CREATED_TX_STAMP, RECIBIR_BODEGAL, RECEPCIONADA_BODEGAL, FECHA_RECEPCION_BODEGAL FROM 
ERP.ORDER_HEADER WHERE STATUS_ID = 'ORDER_COMPLETED' ORDER BY ORDER_DATE DESC

call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse        1      0.03       0.01          0          0          0           0
Execute      1      0.00       0.00          0          0          0           0
Fetch        1      9.10     160.81      66729      65203         37          50
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total        3      9.14     160.83      66729      65203         37          50

Misses in library cache during parse: 1
Optimizer mode: ALL_ROWS
Parsing user id: 58  

Elapsed times include waiting on following events:
  Event waited on                             Times   Max. Wait  Total Waited
  ----------------------------------------   Waited  ----------  ------------
  SQL*Net message to client                       1        0.00          0.00
  db file scattered read                       8178        0.28        146.55
  direct path write temp                       2200        0.04          4.22
  direct path read temp                          36        0.14          2.01
  SQL*Net more data to client                     3        0.00          0.00
  SQL*Net message from client                     1        3.36          3.36
********************************************************************************

所以看来问题是'db file scattered read',如何调整oracle以减少这个事件中的等待有什么想法?

跟进新的tkprof结果,这次关闭会话:

********************************************************************************

SELECT DISTINCT ORDER_ID, ORDER_TYPE_ID, ORDER_NAME, EXTERNAL_ID,
 SALES_CHANNEL_ENUM_ID, ORDER_DATE, ENTRY_DATE, VISIT_ID, STATUS_ID, CREATED_BY,
 FIRST_ATTEMPT_ORDER_ID, CURRENCY_UOM, SYNC_STATUS_ID, BILLING_ACCOUNT_ID,
 ORIGIN_FACILITY_ID, WEB_SITE_ID, PRODUCT_STORE_ID, TERMINAL_ID, TRANSACTION_ID,
 AUTO_ORDER_SHOPPING_LIST_ID, NEEDS_INVENTORY_ISSUANCE, IS_RUSH_ORDER, INTERNAL_CODE,
 REMAINING_SUB_TOTAL, GRAND_TOTAL, LAST_UPDATED_STAMP, LAST_UPDATED_TX_STAMP, CREATED_STAMP,
CREATED_TX_STAMP, RECIBIR_BODEGAL, RECEPCIONADA_BODEGAL, FECHA_RECEPCION_BODEGAL FROM
ERP.ORDER_HEADER WHERE STATUS_ID = 'ORDER_COMPLETED' ORDER BY ORDER_DATE DESC

call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse        1      0.03       0.01          0          0          0           0
Execute      2      0.00       0.00          0          0          0           0
Fetch        1      8.23      47.66      66576      65203         31          50
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total        4      8.26      47.68      66576      65203         31          50

Misses in library cache during parse: 1
Optimizer mode: ALL_ROWS
Parsing user id: 58 

Rows     Row Source Operation
-------  ---------------------------------------------------
     50  SORT ORDER BY (cr=65203 pr=66576 pw=75025 time=47666679 us)
3456659   TABLE ACCESS FULL ORDER_HEADER (cr=65203 pr=65188 pw=0 time=20757300 us)


Elapsed times include waiting on following events:
  Event waited on                             Times   Max. Wait  Total Waited
  ----------------------------------------   Waited  ----------  ------------
  SQL*Net message to client                       1        0.00          0.00
  db file scattered read                       8179        0.14         34.96
  direct path write temp                       2230        0.00          3.91
  direct path read temp                          52        0.14          0.84
  SQL*Net more data to client                     3        0.00          0.00
  SQL*Net message from client                     1     1510.62       1510.62
********************************************************************************

6 个答案:

答案 0 :(得分:2)

由于您按照order_date排序结果,因此您必须在该字段上有降序索引。 还告诉oracle你希望使用这个索引。将order_date字段放在查询的第一位,并使用

之类的提示
SELECT /*+ index(HEADERS IDX_ORDER_DATE_DESC) */ ... 
FROM ERP.ORDER_HEADER HEADERS
WHERE ...
ORDER BY ORDER_DATE DESC

没有那么多关于索引,而是告诉oracle使用它们。甲骨文对指数非常挑剔。根据最重要的查询选择索引时,您可以获得最佳结果。如果有疑问,trace a query。通过这种方式,您可以看到oracle查询的哪个部分花费的时间最多,以及您的索引是否实际被拾取。在解决性能问题时,跟踪是非常宝贵的。

答案 1 :(得分:1)

使用PRIMARY KEY约束将ORDER_ID声明为PK吗?因为如果是这样的话,我希望优化者能够认识到DISTINCT在这个查询中是多余的,并将其优化出来。如果没有这种限制,它就不会知道它是多余的,因此将在“重复删除”结果方面花费不必要的大量努力。

答案 2 :(得分:1)

如果两个查询之间的差异很大,那就太令人惊讶了。您提到没有DISTINCT的查询大约需要30秒。使用DISTINCT的查询需要多长时间?

在使用“alter session set events'10046跟踪名称上下文永远,级别8”跟踪会话后,是否可以使用DISTINCT显示查询的tkprof输出,并在查询完成后断开连接?通过这种方式,我们可以看到实际花费的时间以及它是否在等待某些东西(“直接路径读取温度”可能?)

此致 罗布。


在发布tkprof文件之后进行跟进:

我看到你设法获得tkprof输出,但遗憾的是你没有在创建tkprof文件之前断开你的会话。现在,光标处于打开状态,无法将STAT#行写入跟踪文件。这就是您在tkprof文件中没有计划/行源操作的原因。如果你可以重复这个过程,如果下面的建议结果是垃圾,那将是很好的。

我方面的一点猜测:我认为DISTINCT几乎是一种无操作,因为你选择了很多列。如果这是真的,那么您的谓词“WHERE STATUS_ID ='ORDER_COMPLETED'”非常有选择性,您将从此列上获得索引中受益。创建索引后,请确保正确分析,如果数据值偏斜,甚至可以使用直方图。最终结果将是此查询的不同计划,从INDEX RANGE SCAN开始,然后是TABLE ACCESS BY ROWID,从而导致非常快速的查询。

创建索引后,可以使用以下语句重新分析表格,包括直方图:

exec dbms_stats.gather_table_stats([owner],[table_name],cascade => true,method_opt =>'FOR ALL INDEXED COLUMNS SIZE')

此致 罗布。

答案 3 :(得分:1)

尝试禁用哈希聚合:

select /*+ no_use_hash_aggregation*/ distinct ...

http://oracle-randolf.blogspot.com/2011/01/hash-aggregation.html

答案 4 :(得分:0)

在对我无法控制SQL的应用程序进行故障排除时,我发现dbms_sqltune包可以节省大量时间。请参阅http://download.oracle.com/docs/cd/B28359_01/appdev.111/b28419/d_sqltun.htm,不幸的是,您应该获得许可使用它。

此程序包中有一些过程可以对共享池或AWR存储库中的特定sql_id运行调优分析。如果要对其他索引进行任何改进,则分析将包含索引建议。更重要的是,分析器可能会发现一个改进的访问路径,它可以通过Oracle调用SQL配置文件来实现 - 这是一组提示,只要执行此sql_id,就会存储和使用这些提示。发生这种情况时不需要在SQL语句中对提示进行编码,如果应用程序在语句中生成文字值而不是绑定变量,那么还可以选择执行模糊匹配。

当然,这个工具不应该替代理解应用程序及其数据结构,但是阅读详细说明更好计划(如果找到)的执行路径的输出可能具有教育意义。

答案 5 :(得分:-1)

每次运行查询时,Oracle都在访问整个表(TABLE ACCESS(FULL))。 在STATUS_ID和ORDER_TYPE_ID列上创建INDEX

CREATE INDEX ERP.ORDER_HEADER_I1 ON ERP.ORDER_HEADER ( STATUS_ID, ORDER_TYPE_ID );

会有很多帮助,特别是如果ORDER_HEADER表中有几个不同的STATUS_ID和ORDER_TYPE_ID值。