使Oracle 9i使用索引

时间:2011-05-19 09:12:03

标签: performance oracle oracle9i

在客户端的9i服务器上部署在10g XE上开发的应用时,我遇到了性能问题。相同的查询根据服务器生成完全不同的查询计划:

SELECT DISTINCT FOO.FOO_ID               AS C0,
    GEE.GEE_CODE                         AS C1,
    TO_CHAR(FOO.SOME_DATE, 'DD/MM/YYYY') AS C2,
    TMP_FOO.SORT_ORDER                   AS SORT_ORDER_
FROM TMP_FOO
INNER JOIN FOO ON TMP_FOO.FOO_ID=FOO.FOO_ID
LEFT JOIN BAR ON FOO.FOO_ID=BAR.FOO_ID
LEFT JOIN GEE ON FOO.GEE_ID=GEE.GEE_ID
ORDER BY SORT_ORDER_;

Oracle数据库10g快捷版10.2.0.1.0版 - 生产:

-------------------------------------------------------------------------------------------
| Id  | Operation                       | Name    | Rows  | Bytes | Cost (%CPU)| Time     |
-------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                |         |     1 |    67 |    10  (30)| 00:00:01 |
|   1 |  SORT UNIQUE                    |         |     1 |    67 |     9  (23)| 00:00:01 |
|   2 |   NESTED LOOPS OUTER            |         |     1 |    67 |     8  (13)| 00:00:01 |
|*  3 |    HASH JOIN OUTER              |         |     1 |    48 |     7  (15)| 00:00:01 |
|   4 |     NESTED LOOPS                |         |     1 |    44 |     3   (0)| 00:00:01 |
|   5 |      TABLE ACCESS FULL          | TMP_FOO |     1 |    26 |     2   (0)| 00:00:01 |
|   6 |      TABLE ACCESS BY INDEX ROWID| FOO     |     1 |    18 |     1   (0)| 00:00:01 |
|*  7 |       INDEX UNIQUE SCAN         | FOO_PK  |     1 |       |     0   (0)| 00:00:01 |
|   8 |     TABLE ACCESS FULL           | BAR     |     1 |     4 |     3   (0)| 00:00:01 |
|   9 |    TABLE ACCESS BY INDEX ROWID  | GEE     |     1 |    19 |     1   (0)| 00:00:01 |
|* 10 |     INDEX UNIQUE SCAN           | GEE_PK  |     1 |       |     0   (0)| 00:00:01 |
-------------------------------------------------------------------------------------------

Oracle9i版本9.2.0.1.0 - 64位生产:

----------------------------------------------------------------------------
| Id  | Operation               |  Name    | Rows  | Bytes |TempSpc| Cost  |
----------------------------------------------------------------------------
|   0 | SELECT STATEMENT        |          |    98M|  6546M|       |  3382K|
|   1 |  SORT UNIQUE            |          |    98M|  6546M|    14G|  1692K|
|*  2 |   HASH JOIN OUTER       |          |    98M|  6546M|   137M|  2874 |
|   3 |    VIEW                 |          |  2401K|   109M|       |   677 |
|*  4 |     HASH JOIN OUTER     |          |  2401K|   169M|    40M|   677 |
|   5 |      VIEW               |          |   587K|    34M|       |    24 |
|*  6 |       HASH JOIN         |          |   587K|    34M|       |    24 |
|   7 |        TABLE ACCESS FULL| TMP_FOO  |  8168 |   207K|       |    10 |
|   8 |        TABLE ACCESS FULL| FOO      |  7188 |   245K|       |     9 |
|   9 |      TABLE ACCESS FULL  | BAR      |   409 |  5317 |       |     1 |
|  10 |    TABLE ACCESS FULL    | GEE      |  4084 | 89848 |       |     5 |
----------------------------------------------------------------------------

据我所知,索引存在并且是正确的。我有什么选择让Oracle 9i使用它们?

更新#1: TMP_FOO是一个临时表,在此测试中没有行。 FOO是一个常规表,在我的本地XE中有13,035行;不确定为什么查询计划显示 1 ,也许它意识到针对空表的INNER JOIN不需要全表扫描: - ?

更新#2 我花了几周的时间尝试所有没有提供了真正的增强功能:查询重写,优化提示,数据库设计的变化,摆脱临时表...最后,我得到了客户拥有的相同9.2.0.1.0未修补的Oracle版本的副本(具有明显的架构差异),在我的站点上安装它...惊喜!在我的9i中,所有执行计划都会立即生效,查询需要1到10秒才能完成。

此时,我几乎确信客户存在严重的错误配置问题。

5 个答案:

答案 0 :(得分:6)

看起来您的10g express数据库中没有数据,或者您的统计信息未正确收集。在任何一种情况下,它都会向Oracle看,因为行数不多,因此索引范围扫描是合适的。

在您的9i数据库中,统计信息看起来像是正确收集的,Oracle看到一个包含大量行的4表连接,而没有where子句。在这种情况下,由于您未提供提示,Oracle会使用默认的ALL_ROWS优化程序行为构建解释计划:Oracle将找到将所有行返回到最后行的性能最高的计划。在这种情况下,具有全表扫描的HASH JOIN非常有效,它将使用索引NESTED LOOP连接更快地返回大的行集。

也许您想要使用索引,因为您只对查询的前几行感兴趣。在这种情况下,使用提示/*+ FIRST_ROWS*/,这将有助于Oracle了解您对第一行响应时间比总体查询时间更感兴趣。

也许您想要使用索引,因为您认为这会导致更快的总查询时间。您可以通过使用USE_NLUSE_HASH等提示来强制执行解释计划,但大多数情况下您会看到,如果统计信息是最新的,优化程序将选择最有效的计划


更新:我看到有关TMP_FOO的更新是没有行的临时表。临时表的问题是它们没有统计信息,因此我的上述答案不能完全适用于临时表。由于临时表没有统计数据,因此Oracle必须进行猜测(此处选择非常随意的8168行),这会导致计划效率低下。

这种情况可能适合使用提示。您有几种选择:

  • LEADING,USE_NL和USE_HASH提示的混合可以强制执行特定计划(LEADING设置连接的顺序,USE *设置连接方法)。
  • 您可以使用未记录的CARDINALITY提示向优化程序提供其他信息,如AskTom article中所述。虽然提示没有记录,但可以说是safe to use。注意:在10g +上,DYNAMIC_SAMPLING可能是记录在案的替代方案。
  • 您还可以使用DBMS_STATS.set_table_stats程序预先设置临时表的统计信息。最后一个选项非常激进,因为它可能会修改针对此临时表的所有查询的计划。

答案 1 :(得分:4)

可能是9i做得恰到好处。根据发布的统计数据,Oracle 9i数据库认为它正在处理返回9800万行的语句,而10G数据库认为它将返回1行。可能两者都是正确的,即2个数据库中的数据量非常不同。或者可能是您需要在任一数据库或两个数据库中收集统计信息以获得更准确的查询计划。

答案 2 :(得分:3)

通常,当目标版本较旧且版本不同时,很难调整查询。如果没有真实的数据量,或者至少现实统计,您就无法调优查询。

如果您与客户建立了良好关系,可以要求他们使用DBMS_STATS.EXPORT_SCHEMA_STATS()导出统计信息。然后,您可以使用匹配的IMPORT_SCHEMA_STATS过程导入统计信息。

否则,您必须使用DBMS_STATS.SET_TABLE_STATISTICS()过程自行伪造数字。 Find out more

答案 3 :(得分:1)

您可以添加以下提示,“强制”Oracle使用您的索引(如果可能):

Select /*+ index (FOO FOO_PK) */
       /*+ index (GEE GEE_PK) */
From ...

或尝试使用FIRST_ROWS提示表明你不会获取所有这些估计的98百万行...否则我怀疑索引会产生巨大的差异因为你没有Where子句所以Oracle必须阅读无论如何这些表。

答案 4 :(得分:0)

客户更改了默认设置以支持非常旧的第三方遗留应用程序:静态参数OPTIMIZER_FEATURES_ENABLE已从9i(9.2.0)中的默认值更改为{{ 1}}。

我在9i的本地副本中做了同样的更改,我遇到了同样的问题:解释需要花费数小时计算的计划等等。

(知道这一点,我问了一个related question at ServerFault,但我相信这解决了原来的问题。)