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

发布于 2024-08-11 18:16:56 字数 925 浏览 5 评论 0原文

我有这个表:

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 不再喜欢该索引。

第二天早上,Oracle 突然喜欢上了这个索引(可能是睡过头了)并开始使用它,但时间不长。一段时间后,处理速度从 50 行/秒下降到 3 行/秒,我再次看到“全表扫描”。到底是怎么回事?

就我而言,我需要处理大约一百万行。我批量提交更改。 50. 在提交更新/重组索引或类似的命令后,我应该运行一些命令吗?

我使用的是 Oracle 10g。

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

I have this table:

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

and this index:

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

When I run this query

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

EXPLAIN PLAN shows that it does a full table scan. So I dropped the index and created it again. EXPLAIN PLAN still says full table scan. How can that be?

Some background: This is historical data, so what happens is that I look up a row with state CLEARED and insert a new row with state NEW (plus I copy a few values from the old row). The old row is then updated to USED. So the table always grows. What I did notice is that the cardinality of the index is 0 (despite the fact that I have thousands of different values). After recreating, the cardinality grew but the CBO didn't like the index any better.

The next morning, Oracle suddenly liked the index (probably slept over it) and started to use it but not for long. After a while, the processing dropped from 50 rows/s to 3 rows/s and I saw again "FULL TABLE SCAN". What is going on?

In my case, I need to process about a million rows. I commit the changes in batches of ca. 50. Is there some command which I should run after a commit to update/reorg the index or something like that?

I'm on Oracle 10g.

[EDIT] I have 969'491 distinct keys in this table, 3 types and 3 states.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

请止步禁区 2024-08-18 18:16:56

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

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

听起来好像一夜之间发生的事情就是对表格进行了分析。然后,当您对表运行处理时,足够的索引被更新,导致 Oracle 表的统计信息再次过时,优化器停止使用索引。

添加提示并查看 EXPLAIN PLAN 是否为您提供不同的计划并且查询性能更好。

哦,托尼关于分析表的回答是一个普遍的好习惯,尽管 10g 的数据库在这方面进行自我维护非常好。如果您的进程正在进行大量更新,索引可能很快就会过时。如果当你的进程开始陷入困境时运行分析可以暂时改善情况,那么你就会知道这就是问题所在。

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

例如:

exec dbms_stats.gather_table_stats('所有者','DEMO');

What happens if you specify an index hint? Try this:

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

It sounds like what happened overnight was that the table got analyzed. Then, as you ran your processing against the table, enough of the index was updated to cause oracle's table's statistics to go stale again and the optimizer stopped using the index.

Add the hint and see if EXPLAIN PLAN gives you a different plan and the query performs better.

Oh, and Tony's answer regarding analyzing the table is a general good practice, although with 10g the database is pretty good about doing self-maintenance in that regard. If your process is doing a lot of updates the index can go stale quickly. If running analyze when your process starts going in the ditch improves the situation for a while, you would then know this is the problem.

To update statistics for the table, use the dmbs_stats.gather_table_stats package.

For example:

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

闻呓 2024-08-18 18:16:56

最近是否分析过该表?如果Oracle认为它很小它甚至可能不会考虑使用索引。

试试这个:

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

NUM_ROWS 告诉您 Oracle 认为该表包含多少行。

Has the table been analyzed recently? If Oracle thinks it is very small it may not even consider using the index.

Try this:

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

NUM_ROWS tells you how many rows Oracle thinks the table contains.

小…楫夜泊 2024-08-18 18:16:56

“第二天早上,Oracle 突然喜欢上了这个索引(可能是睡过头了)”
可能 DBMS_STATS 正在运行过夜。

一般来说,我会看到对索引进行全表扫描的三个原因之一。第一个是优化器认为表是空的,或者至少非常小。我怀疑这是最初的问题。在这种情况下,全面扫描仅包含少数块的表比使用索引会更快。

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

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

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

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

"The next morning, Oracle suddenly liked the index (probably slept over it)"
Probably a DBMS_STATS is running overnight.

Generally I would see one of three reasons for a FULL TABLE SCAN over an index. The first is that the optimizer thinks the table is empty, or at least very small. I suspect this was the initial problem. In that case it would be quicker to full scan a table consisting of only a handful of blocks rather than use an index.

The second is when the query is such that an index cannot be practically used.

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

Are you actually using literals hard-coded in the query. If not, your variable datatypes may be incorrect (eg key being character). That would require the numeric key be converted to character for comparison, which would make the index nearly useless.

The third reason is where it thinks the query will process a large proportion of the rows in the table. Type and State seem pretty low cardinality. Do you perhaps have a large number of a specific 'key' value ?

优雅的叶子 2024-08-18 18:16:56

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

A comment on the processing you describe: it sounds like you are doing row-by-row processing with intermittent commits, and I'd urge you to rethink this if you can. The update/insert mechanism might well be converted to a MERGE statement and the entire data set can then be processed in a single statement with one commit at the end. This would almost certainly be faster and use less resources than your current method.

追我者格杀勿论 2024-08-18 18:16:56

列键的值总是1吗?如果是这样,我不确定查询索引是否会优化查询,因为无论如何都必须检查每一行。如果是这样,则声明不带键列的索引。您也可以尝试:

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

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

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

Is the value of the column key always 1? If so, I'm not sure that consulting the index would optimize the query, since each row would have to be examined anyway. If so, declare the index without the key column. You could also try:

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

which (if my guess is right) would still need to look at each row, but which might go to the index since all columns in the result set are now covered.

I'm just guessing based on your statement that the index shows cardinality 0.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文