为什么Oracle会忽略“完美”指数?

时间:2009-11-14 14:29:53

标签: oracle indexing full-table-scan

我有这张桌子:

create table demo (
    key number(10) not null,
    type varchar2(3) not null,
    state varchar2(16) not null,
    ... lots more columns ...
)

和这个索引:

create index demo_x04 on demo(key, type, state);

当我运行此查询时

select * from demo where key = 1 and type = '003' and state = 'NEW'

EXPLAIN PLAN表示它会执行全表扫描。所以我放弃了索引并再次创建它。 EXPLAIN PLAN仍然表示全表扫描。怎么会这样?

一些背景:这是历史数据,所以会发生的事情是我查找状态为CLEARED的行并插入状态为NEW的新行(另外我复制了一些旧的值)行)。然后将旧行更新为USED。因此桌子总是在增长。我注意到的是索引的基数是0(尽管事实上我有数千个不同的值)。在重新创建之后,基数增长了但是CBO并不喜欢这个指数。

第二天早上,甲骨文突然喜欢这个索引(可能已经睡过了它)并且开始使用它但不是很长时间。过了一会儿,处理从50行/秒下降到3行/秒,我再次看到“FULL TABLE SCAN”。发生了什么事?

就我而言,我需要处理大约一百万行。我批量提交变更。 50.在提交更新/重组索引之后是否应该运行一些命令?

我使用的是Oracle 10g。

[编辑]我在这个表中有969'491个不同的键,有3种类型和3种状态。

5 个答案:

答案 0 :(得分:4)

如果指定索引提示会发生什么?试试这个:

SELECT /*+ INDEX (demo demo_x04) */ * 
  FROM demo 
 WHERE key = 1 
   AND type = '003' 
   AND state = 'NEW';

这听起来像是一夜之间发生的事情,表格得到了分析。然后,当您对表运行处理时,更新了足够的索引以使oracle表的统计信息再次失效并且优化器停止使用索引。

添加提示,看看EXPLAIN PLAN是否为您提供了不同的计划,查询效果更好。

哦,Tony关于分析表的答案是一般的良好做法,尽管10g数据库在这方面做自我维护是相当不错的。如果您的进程正在进行大量更新,则索引可能会很快失效。如果在流程开始进入沟渠时运行分析会改善情况一段时间,那么您就会知道这就是问题。

要更新表格的统计信息,请使用dmbs_stats.gather_table_stats包。

例如:

exec dbms_stats.gather_table_stats('the owner','DEMO');

答案 1 :(得分:3)

最近是否对表格进行了分析?如果Oracle认为它非常小,甚至可能不考虑使用索引。

试试这个:

select last_analyzed, num_rows 
from user_tables
where table_name = 'DEMO';

NUM_ROWS告诉您Oracle认为该表包含的行数。

答案 2 :(得分:3)

“第二天早上,甲骨文突然喜欢这个指数(可能已经睡过了)” 可能是DBMS_STATS在一夜之间运行。

通常我会看到对索引进行FULL TABLE扫描的三个原因之一。第一个是优化器认为表是空的,或者至少非常小。我怀疑这是最初的问题。在这种情况下,完全扫描一个只包含少量块而不是使用索引的表会更快。

第二种是当查询无法实际使用索引时。

"select * from demo where key = 1 and type = '003' and state = 'NEW'"

您实际上是在查询中使用硬编码的文字吗?如果不是,则您的变量数据类型可能不正确(例如,键是字符)。这将需要将数字键转换为字符进行比较,这将使索引几乎无用。

第三个原因是它认为查询将处理表中的大部分行。类型和状态似乎相当低基数。您是否拥有大量特定的“关键”值?

答案 3 :(得分:1)

对您描述的处理的评论:听起来您正在进行间歇性提交的逐行处理,如果可以,我建议您重新考虑这一点。更新/插入机制可能会转换为MERGE语句,然后可以在单个语句中处理整个数据集,最后一次提交。这几乎肯定比现有方法更快,使用的资源更少。

答案 4 :(得分:0)

列键的值是否始终为1?如果是这样,我不确定咨询索引会优化查询,因为无论如何都必须检查每一行。如果是这样,请声明没有键列的索引。你也可以尝试:

select key, type, state from demo where key = 1 and type = '003' and state = 'NEW'

(如果我的猜测是正确的)仍然需要查看每一行,但可能会转到索引,因为现在覆盖了结果集中的所有列。

我只是根据你的陈述猜测索引显示基数为0。