重建索引时资源繁忙

时间:2013-05-17 06:14:49

标签: sql oracle oracle10g indexing locking

有一个表T,其中包含a列:

CREATE TABLE T {
  id_t integer not null,
  text varchar2(100),
  a integer
} 
/

ALTER TABLE T ADD CONSTRAINT PK_T PRIMARY KEY (ID_T)
/

索引是这样创建的:

CREATE INDEX IDX_T$A ON T(a);

还有这样的检查约束:

ALTER TABLE T ADD CONSTRAINT CHECK (a is null or a = 1);

T中的大多数记录都具有a的空值,因此如果索引处于一致状态并且其统计信息是最新的,则使用索引的查询工作得非常快。

但问题是某些行的a的值经常变化(某些行获取空值,有些行获得1),我需要重建索引,让我们说每小时一次。

然而,实际上当这样做的时候,尝试重建索引时,它会得到一个例外:

ORA-00054: resource busy and acquire with NOWAIT specified

有人可以帮我解决这个问题吗?

4 个答案:

答案 0 :(得分:1)

您是否尝试将“ONLINE”添加到该索引重建语句?

编辑:如果在线重建不可用,那么您可以查看提交物化视图上的快速刷新,以存储列A的行的rowid或主键。

首先看一下文档: -

http://docs.oracle.com/cd/B28359_01/server.111/b28326/repmview.htm http://docs.oracle.com/cd/B28359_01/server.111/b28286/statements_6002.htm#SQLRF01302

您将在表格上创建物化视图日志,然后创建物化视图。

特别考虑对此的资源要求:对主表的更改需要将更改向量写入实体化视图日志,这实际上是每次更改的附加插入。然后,必须使用其他查询将更改传播到另一个表(物化视图存储表)。这绝不是一个低影响力的选择。

答案 1 :(得分:1)

大多数情况下不需要重建索引。当然,新创建的索引是有效的,其效率会随着时间的推移而降低但是这个过程在一段时间后停止了 - 它只是收敛到某种程度。

如果您确实需要优化索引,请尝试使用侵入性较小的DDL命令“ALTER INDEX SHRINK SPACE COMPACT”。

PS:我还建议您使用一些较小的块大小(4K或8K)进行表空间存储。

答案 2 :(得分:1)

重建效果

大多数Oracle专家对频繁重建索引持怀疑态度。例如,快速浏览一下演示文稿Rebuilding the Truth将向您显示索引的行为并不像许多人认为的那样天真。

该演示文稿中的一个相关点是“完全删除的块被回收并且通常不存在问题”。如果您的值完全改变,那么您的索引不应该无限大。虽然您的索引是以非典型的方式使用的,但是 行为可能是一件好事。

这是一个简单的例子。创建100万行并索引其中的100行。

--Create table, constraints, and index.
CREATE TABLE T
(
    id_t integer primary key,
    text varchar2(100),
    a integer check (a is null or a = 1)
);

CREATE INDEX IDX_T$A ON T(a);

--Insert 1M rows, with 100 "1"s.
insert into t
select level, level, case when mod(level, 10000) = 0 then 1 else null end
from dual connect by level <= 1000000;
commit;

--Initial sizes:
select segment_name, bytes/1024/1024 MB
from dba_segments
where segment_name in ('T', 'IDX_T$A');

SEGMENT_NAME    MB
T               19
IDX_T$A         0.0625

现在将索引行完全洗牌大约1000次。

--Move the 1s around 1000 times.  Takes about 6 minutes.
begin
    for i in 9000 .. 10000 loop
        update t
        set a = case when mod(id_t, i) = 0 then 1 else null end
        --Don't update if the vlaue is the same
        where nvl(a,-1) <> nvl(case when mod(id_t,i) = 0 then 1 else null end,-1);
        commit;
    end loop;
end;
/

索引段的大小仍然相同。

--The the index size is the same.
select segment_name, bytes/1024/1024 MB
from dba_segments
where segment_name in ('T', 'IDX_T$A');

SEGMENT_NAME    MB
T               19
IDX_T$A         0.0625

重建统计数据

最好担心数据变化如此剧烈的对象的统计数据。但同样,虽然您的系统很不寻常,但它可以在默认的Oracle行为中正常工作。尽管索引的行可能完全改变,但相关的统计数据可能保持不变。如果索引总是有100行,则行数,块数和清晰度将保持不变。

如果100行从完全随机变为非常接近,则聚类因子可能会发生显着变化。但即便如此也许并不重要。如果有数百万行,但只有100个索引,那么无论聚类因素如何,优化器的决策都可能相同。读取1个块(令人敬畏的聚类因子)或读取100个块(最坏情况的聚类因子)仍然比进行数百万行的全表扫描要好得多。

但统计数据很复杂,我肯定过度简化了事情。如果您需要以特定方式保留统计信息,则可能需要锁定它们。不幸的是,你不能只锁定一个索引,但你可以锁定表及其依赖索引。

begin
    dbms_stats.lock_table_stats(ownname => user, tabname => 'T');
end;
/
无论如何

重建

如果仍然需要重建,@ Robert Eleckers重试的想法应该有效。虽然不是例外,但设置DDL_LOCK_TIMEOUT会更容易。

alter session set ddl_lock_timeout = 500;

会话仍然需要对表进行独占锁定,但这样可以更容易地找到合适的机会窗口。

答案 3 :(得分:0)

由于相关字段的基数非常低,我建议使用位图索引并完全跳过重建。

CREATE BITMAP INDEX IDX_T$A ON T(a);

注意(如评论中所述):位图索引的事务性能非常低,因此只有在对表进行更新的重叠事务很少时,这才能正常工作。