我们有两个表,如下所示:

Event
    id
    type
    ... a bunch of other columns

ProcessedEvent
    event_id
    process

定义了索引

  • 事件(id)(PK)
  • ProcessedEvent(event_id,进程)

第一个表示应用程序中的事件。

第二个表示某个事件由某个进程得到处理的事实。有许多进程需要处理某个事件,因此第一个表中的每个条目在第二个表中都有多个条目。

为了找到所有需要处理的事件,我们执行以下查询:

select * // of course we do name the columns in the production code
from Event
where type in ( 'typeA', 'typeB', 'typeC')
and id not in (
    select event_id
    from ProcessedEvent
    where process = :1  
)

统计数据是最新的

由于大多数事件都已处理,我认为最好的执行计划应该是这样的

  • ProcessedEvent 索引上的完整索引扫描
  • 对事件索引进行全索引扫描
  • 两者之间的反连接
  • 与其余的表访问
  • 筛选

相反,Oracle 执行以下操作

  • ProcessedEvent 索引上的完整索引扫描
  • 对事件表进行全表扫描
  • 过滤事件表
  • 两个集合之间的反连接

通过索引提示,我让 Oracle 执行以下操作:

  • ProcessedEvent 索引上的完整索引扫描
  • 对事件索引进行全索引扫描
  • 事件表上的表访问
  • 过滤事件表
  • 两个集合之间的反连接

恕我直言,这真的很愚蠢。

所以我的问题是:oracle 坚持早期表访问的原因可能是什么?


添加:表现很差。我们通过仅选择 Event.ID,然后“手动”获取所需的行来解决性能问题。但当然这只是一种解决方法。

有帮助吗?

解决方案

您的完整索引扫描可能会比完整表扫描更快,因为索引可能比表“更薄”。不过,全索引扫描是完整的段读取,其成本与全表扫描大致相同。

但是,您还添加了 TABLE ACCESS BY ROWID 步骤。这是一个昂贵的步骤:1个逻辑IO 每行 用于 ROWID 访问,而您获得一个逻辑 IO 每多块 (取决于您的 db_file_multiblock_read_count parameter)用于全表扫描。

总之,优化器计算得出:

cost(FULL TABLE SCAN) < cost(FULL INDEX SCAN) + cost(TABLE ACCESS BY ROWID)

更新:FULL TABLE SCAN 还比 FULL INDEX SCAN 路径更快地启用类型过滤器(因为 INDEX 不知道事件是什么类型),因此减少了将被反连接的集合的大小(另一个优点全表扫描)。

其他提示

优化器做了很多一开始没有意义的事情,但它有它的理由。他们可能并不总是 正确的, ,但它们是可以理解的。

由于事件表的大小,它可能比通过 rowid 访问更容易进行全面扫描。顺序读取整个表所涉及的 IO 操作可能比读取位和片段少得多。

性能是否很差,或者您只是想问优化器为什么这样做?

我无法解释优化器的行为,但我的经验是不惜一切代价避免“NOT IN”,而是用 MINUS 替换它,如下所示:

select * from Event
where id in (
  select id from Event where type in ( 'typeA', 'typeB', 'typeC')
 minus
  select id from ProcessedEvent
)

我已经看到类似转换的查询性能提高了几个数量级。

就像是:

WITH
  PROCEEDED AS
  (
    SELECT
      event_id
    FROM
      ProcessedEvent
    WHERE
      PROCESS = :1
  )
SELECT
  * // of course we do name the columns in the production code
FROM
  EVENT
LEFT JOIN PROCEEDED P
ON
  p.event_id = EVENT.event_id
WHERE
  type           IN ( 'typeA', 'typeB', 'typeC')
  AND p.event_id IS NULL; -- exclude already proceeded

可以足够快地工作(至少比 NOT IN).

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top