5

突然(但不幸的是,我不知道“突然”是什么时候;我知道它在过去的某个时候运行良好)我的一个查询开始需要 7 多秒而不是毫秒来执行。我有 1 个本地表和 3 个通过 DB 链接访问的表。3 个远程表连接在一起,其中一个与我的本地表连接。

本地表的 where 子句只需要几毫秒即可自行执行,并且只返回几条(最多 10 或 100 条)记录。3 个远程表之间有数十万甚至数百万条记录,如果我适当地加入它们,我会得到数万或数十万条记录。

我只加入远程表,以便我可以提取与本地表中每条记录相关的几条数据。

然而,似乎正在发生的事情是,Oracle 首先将远程表连接在一起,然后将我的本地表连接到最后。这总是一个坏主意,特别是考虑到现在存在的数据集,所以我/*+ LEADING(local_tab remote_tab_1) */在我的查询中添加了一个提示,它现在以毫秒为单位返回。

我比较了解释计划,它们几乎相同,除了一个BUFFER SORT远程表上的一个。

我想知道什么可能导致 Oracle 以错误的方式处理这个问题?是索引问题吗?我应该寻找什么?

4

5 回答 5

5

在选择执行计划时,oracle 会估算不同计划的成本。该估计的一个关键信息是将从执行计划的一个步骤返回的行数。Oracle 尝试使用“统计”来估计那些信息,即有关表包含多少行、列包含多少不同值的信息;这些值分布的均匀程度。

这些统计只是那个统计,可能是错误的,这也是预言机优化器误判的最重要原因之一。

因此,按照评论中的描述收集新的统计数据可能会有所帮助。查看该 dbms_stats 包的文档。有许多不同的方法可以调用该包。

于 2010-02-23T21:28:21.303 回答
3

我遇到的一个常见问题是连接许多表的查询,其中连接形成从一端到另一端的链,例如:

SELECT *
FROM   tableA, tableB, tableC, tableD, tableE
WHERE  tableA.ID0 = :bind1
AND    tableA.ID1 = tableB.ID1
AND    tableB.ID2 = tableC.ID2
AND    tableC.ID3 = tableD.ID3
AND    tableD.ID4 = tableE.ID4
AND    tableE.ID5 = :bind2;

请注意优化器如何选择从 tableA(例如,如果 ID0 上的索引具有很好的选择性)或从 tableE(如果 tableE.ID5 上的索引更具选择性)驱动查询。

桌子上的统计数据可能会导致这两个计划之间的选择在刀刃上取得平衡;有一天它工作正常(从 tableA 驱动),第二天收集了新的统计数据,突然之间,从 tableE 驱动的替代计划成本更低并被选中。

在这种情况下,添加一个 LEADING 提示是一种将其推回原始计划(即从 tableA 驱动)的方法,而不会对优化器做出太多规定(即它不会强制优化器选择任何特定的连接方法)。

于 2010-02-24T07:46:35.300 回答
2

你正在做分布式查询优化,这是一个棘手的问题。可能是您的表的统计信息是最新的,但现在远程系统上的表不正常或已更改。或者远程系统添加/删除/修改了索引,这破坏了你的计划。(这是考虑复制的一个很好的理由——因此您可以控制索引和统计数据。)

也就是说,Oracle 对基数的估计是执行计划的主要驱动力。10053 跟踪分析(Jonathan Lewis 的基于成本的 Oracle Fundamentals 书中有从 8i 到 10.1 的精彩示例)可以帮助阐明您的语句现在损坏的原因以及LEADING提示如何修复它。

DRIVING_SITE如果您知道总是希望在访问远程站点之前先连接本地表,则提示可能是更好的选择;LEADING它澄清了你的意图,而不像暗示那样推动计划。

于 2010-02-24T15:26:50.600 回答
1

可能不相关,但我曾经遇到过类似的情况,远程表已被单表视图替换。当它是一个表时,分布式查询优化器“看到”它有一个索引。当它变成一个视图时,它再也看不到索引,也不能花费在远程对象上使用索引的计划。

那是几年前的事了。我当时在这里记录了我的分析。

于 2010-02-24T22:17:07.430 回答
1

RI,

不查看 SQL 就很难确定性能问题的原因。

当一个 Oracle 查询之前表现良好,突然开始表现不佳时,通常与以下两个问题之一有关:

A) 统计数据已过时。这是最容易和最快的检查方法,即使您有一个应该处理它的内务批处理过程......总是仔细检查。

B) 数据量/数据模式变化。

在您的情况下,跨多个数据库运行分布式查询会使 Oracle 管理它们之间的性能变得更加困难 10 倍。是否可以将这些表放在一个数据库中,或者将不同的模式所有者放在一个数据库中?

众所周知,提示很脆弱,因为 Oracle 没有义务遵循提示。当数据量或模式发生更多变化时,Oracle 可能会忽略提示并做它认为最好的事情(即最差的;-)。

如果您不能将这些表全部放在一个数据库中,那么我建议您将查询分成两个语句:

  1. 在 sub-SELECT 上插入以将外部数据复制到当前数据库中的全局临时表。
  2. 从全局临时表中选择以与您的其他表连接。

您将完全控制上述步骤 1 的性能,而无需求助于提示。这种方法通常可以很好地扩展,让您花时间进行性能调整。我已经看到这种方法解决了许多复杂的性能问题。

Oracle 创建全新表或插入记录堆的开销比大多数人预期的要小得多。定义一个全局临时表进一步减少了这种开销。

马修

于 2010-02-25T14:45:58.367 回答