我们有两个这样的表:
Event
id
type
... a bunch of other columns
ProcessedEvent
event_id
process
为
定义了索引第一个代表应用程序中的事件。
第二个代表某个事件由某个过程获得过程的事实。有许多进程需要处理某个事件,因此第二个表中的第一个条目中有多个条目。
为了找到需要处理的所有事件,我们执行以下查询:
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
)
统计数据是最新的
由于大多数事件都已处理,我认为最好的执行计划看起来应该是这样的
相反,Oracle会执行以下操作
通过索引提示,Oracle让Oracle执行以下操作:
这真是愚蠢的恕我直言。
所以我的问题是:oracle坚持早期表访问的原因可能是什么?
增加: 表现不好。我们通过仅选择Event.ID然后“手动”获取所需的行来解决性能问题。但当然这只是一种解决方法。
答案 0 :(得分:2)
您的FULL INDEX SCAN可能比FULL TABLE SCAN更快,因为索引可能比表更“稀”。尽管如此,FULL INDEX SCAN是一个完整的段读数,它与FULL TABLE SCAN的成本大致相同。
但是,您还要添加TABLE ACCESS BY ROWID步骤。这是一个昂贵的步骤:ROWID访问的一个逻辑IO 每行,而每个多个块获得一个逻辑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不知道事件的类型),因此减小了将被反连接的集合(FULL TABLE SCAN的另一个优点)。
答案 1 :(得分:0)
优化器做了很多事情,一开始没有意义,但它有它的原因。它们可能并非总是正确,但它们是可以理解的。
由于其大小,Event表可能更容易进行全扫描而不是通过rowid访问。可能是,按顺序读取整个表所涉及的IO操作要少得多,而不是读取位和碎片。
性能不好,或者您只是问为什么优化器会这样做?
答案 2 :(得分:0)
我无法解释优化器的行为,但我的经验是不惜一切代价避免“NOT IN”,而是用MINUS替换它,如下所示:
select * from Event
where id in (
select id from Event where type in ( 'typeA', 'typeB', 'typeC')
minus
select id from ProcessedEvent
)
我已经看到了具有类似转换的查询性能的数量级。
答案 3 :(得分:0)
类似的东西:
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
快得多)。