使Oracle 9i使用索引

Álv*_*lez 2 oracle performance 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_;
Run Code Online (Sandbox Code Playgroud)

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 |
-------------------------------------------------------------------------------------------
Run Code Online (Sandbox Code Playgroud)

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 |
----------------------------------------------------------------------------
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

Vin*_*rat 6

看起来你要么没有10g express数据库的数据,要么你的统计数据没有正确收集.在任何一种情况下,它都会向Oracle看起来没有很多行,因此索引范围扫描是合适的.

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

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

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


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

这种情况可能适合使用提示.你有几个选择:

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