在客户端的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秒才能完成。
此时,我几乎确信客户存在严重的错误配置问题。
答案 0 :(得分:6)
看起来您的10g express数据库中没有数据,或者您的统计信息未正确收集。在任何一种情况下,它都会向Oracle看,因为行数不多,因此索引范围扫描是合适的。
在您的9i数据库中,统计信息看起来像是正确收集的,Oracle看到一个包含大量行的4表连接,而没有where子句。在这种情况下,由于您未提供提示,Oracle会使用默认的ALL_ROWS优化程序行为构建解释计划:Oracle将找到将所有行返回到最后行的性能最高的计划。在这种情况下,具有全表扫描的HASH JOIN非常有效,它将使用索引NESTED LOOP连接更快地返回大的行集。
也许您想要使用索引,因为您只对查询的前几行感兴趣。在这种情况下,使用提示/*+ FIRST_ROWS*/
,这将有助于Oracle了解您对第一行响应时间比总体查询时间更感兴趣。
也许您想要使用索引,因为您认为这会导致更快的总查询时间。您可以通过使用USE_NL
和USE_HASH
等提示来强制执行解释计划,但大多数情况下您会看到,如果统计信息是最新的,优化程序将选择最有效的计划
更新:我看到有关TMP_FOO的更新是没有行的临时表。临时表的问题是它们没有统计信息,因此我的上述答案不能完全适用于临时表。由于临时表没有统计数据,因此Oracle必须进行猜测(此处选择非常随意的8168行),这会导致计划效率低下。
这种情况可能适合使用提示。您有几种选择:
答案 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,但我相信这解决了原来的问题。)