【问题标题】:resource busy while rebuilding an index重建索引时资源繁忙
【发布时间】:2013-05-12 05:48:54
【问题描述】:

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

谁能帮我解决这个问题?

【问题讨论】:

  • 您可以使用重试机制来处理它。捕获资源繁忙异常并重试直到成功。
  • 快速变化对您的索引有什么影响,从而推动了这些频繁重建的需求?
  • 关于它的统计信息很快就会过期。重建重新收集统计信息并使索引更小(读取查询变得更快)。

标签: sql oracle oracle10g indexing locking


【解决方案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

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

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

【讨论】:

  • 在线重建是对我不可用的企业选项。
  • 您能否举例说明您对实体化视图的含义?看起来真不错。
【解决方案2】:

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

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

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

【讨论】:

  • 不过,如果值从多个会话中快速变化,这对于并发性将是相当不利的。
  • 如果数据由多个进程同时更改并且非常频繁,则使用位图索引是absolutely not recommended
  • 顺便说一句select * from v$option where parameter = 'Bit-mapped indexes' 说它在标准版中也不可用..
【解决方案3】:

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

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

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

【讨论】:

  • 想象一下这样一种情况:表中有数百万行,其中只有一百行没有索引列的空值,一个小时后,非空值有完全不同的行(以前的行有空值在其中),您确定索引不会继续永久增长并且效率越来越低吗?
  • Oracle 索引是自我碎片整理的。至少在某种程度上。它还取决于索引列的顺序。如果索引不适合您的搜索需求,请查找“星型设计模式” - 它用于数据仓库系统。这很简单。或者还有一个“压缩”索引的选项。
  • 还有一个注释,索引“大小”可以用不同的指标来表示。段大小 - 索引占用多少字节。叶块数 - 索引有多少叶块。他们每个人都可以有自己的意义。 (顺便说一句:索引由分支块、叶块和空闲块组成)
  • In another question 我在问shrink space compactcoalesce 之间的区别,因为文档中没有足够的信息。发现Tom对这两个命令都进行了测试,发现收缩仍然锁表。那么,如果我们不讨论索引的物理位置,那么与rebuild 的区别是什么?如果您愿意,可以在此处添加您的意见。
  • 你说得对,coleasce 对你来说是更好的选择。 REBUILD 通常必须在 TEMP 表空间中分配一些空间(这需要额外的磁盘 IO),并且还必须对表中的记录进行排序。 SHRINK 和 COALESCE 遍历树的叶子页面并优化它们的空间使用。两者都减少了叶块的数量,SHRINK 也减少了索引段的大小。
【解决方案4】:

为性能而重建

大多数 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;

会话仍需要在表上获得排他锁,但这将更容易找到合适的机会窗口。

【讨论】:

    猜你喜欢
    • 2015-06-03
    • 1970-01-01
    • 2011-07-02
    • 2017-09-02
    • 1970-01-01
    • 2016-08-29
    • 2015-09-24
    • 1970-01-01
    • 2011-09-04
    相关资源
    最近更新 更多