1

有一个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

4 回答 4

1

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

编辑:如果在线重建不可用,那么您可能会查看提交物化视图的快速刷新,以存储列 A 为 1 的行的 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

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

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

于 2013-05-17T06:26:10.827 回答
1

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

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

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

于 2013-05-17T19:44:56.363 回答
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;
/

无论如何都要重建

如果仍然需要重建,@Robe Eleckers 重试的想法应该可行。尽管设置DDL_LOCK_TIMEOUT会更容易,而不是异常。

alter session set ddl_lock_timeout = 500;

会话仍然需要在表上获得排他锁,但这将使找到正确的机会窗口变得更加容易。

于 2013-05-21T04:04:13.970 回答
0

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

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

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

于 2013-05-17T07:01:45.580 回答