有一个表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
有人可以帮我解决这个问题吗?
答案 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);
注意(如评论中所述):位图索引的事务性能非常低,因此只有在对表进行更新的重叠事务很少时,这才能正常工作。