oracle索引失效的原因


非分区索知引的话
select index_name,status from user_indexes 查看状态或者 dba_indexes
分区索引的道话
查看 user_ind_partitions或者dba_ind_partitions
如果还内有二级分区,容需要查看
user_ind_subpartitions 或者dba_ind_subpartitions

分区表中 local 索引的维护会在oracle 操作表分区的时候自动进行,需要注意的是global 索引,当global索引所在表执行alter table 涉及下列操作时,会导至该索引失效,需要重新建立:

Ø ADD PARTITION | SUBPARTITION

Ø COALESCE PARTITION | SUBPARTITION

Ø DROP PARTITION | SUBPARTITION

Ø EXCHANGE PARTITION | SUBPARTITION

Ø MERGE PARTITION | SUBPARTITION

Ø MOVE PARTITION | SUBPARTITION

Ø SPLIT PARTITION | SUBPARTITION

Ø TRUNCATE PARTITION | SUBPARTITION

       因此,建议用户在执行上述操作sql 语句后附加update indexes 子句,oracle即会自动维护全局索引,当然,需要注意这中间有一个平衡,你要平衡操作ddl 的时间和重建索引哪个时间更少,以决定是否需要附加updateindexes 子句。

 

 

 

 

总结:  ----不管是全局索引和本地索引,只要出现了数据移动,那么索引或分区索引都会失效

1、执行alter table add partition 时未指定update indexes 子句:

a. 如果是range/list分区,其local 索引和global 索引不会受影响;

b. 如果是hash 分区,新加分区及有数据移动的分区的local 索引和global索引会被置为unuseable,需要重新编译。

2、在执行drop partition时如果没有指定update indexes 子句:

       会导致glocal索引的失效,对于local索引,删除分区时对应的索引分区会被同时删除,且它分区的local索引不会受到影响。

3、在执行split partition/subpartition 时,如果没有指定update indexes 子句:

       都会造成local 和global 索引的失效。不过如果你split partition/subpartition 的是个空分区,或者没有触发任何数据移动或变化,那么即使不加update indexes,也不会影响到索引。当然,保险起见,建议你还是执行完之后,查询一下数据字典,确认一下当前索引的状态。

 

 

 

 

如果按照索引是否分区作为划分依据,Oracle 的索引类型可以分为非分区索引(普通索引和全局分区索引),全局分区索引,和本地分区索引(带分区列的本地分区索引和不带分区列的本地分区索引)。涉及到删除分区,都使用本地分区索引,这样才不会导致其它的索引失效

1. 非分区表可以创建分区索引。

2. 非分区表的分区索引必须是GLOBAL。

3. 非分区表的分区索引,可以是哈希全局分区索引、全局范围分区索引,但不可以是全局列表分区索引

----------------------------------------------------------------------

查询失效的索引并进行删除和在创建脚本如下

ey:

SELECT 'DROP INDEX ' || T.TABLE_OWNER || '.' || INDEX_NAME || ';' ||

CHR(10) || 'CREATE INDEX ' || T.TABLE_OWNER || '.' || INDEX_NAME ||

' ON ' || T.TABLE_OWNER || '.' || T.TABLE_NAME || '(' || T.IX_CL || ')' ||

' TABLESPACE ' || T.TABLE_OWNER || ' local NOLOGGING PARALLEL 16;'

  FROM (SELECT I.TABLE_OWNER, I.TABLE_NAME, I.INDEX_NAME, C.IX_CL AS IX_CL

          FROM DBA_INDEXES I,

               (SELECT CL.TABLE_OWNER AS TABLE_OWNER,

                       CL.TABLE_NAME AS TABLE_NAME,

                       CL.INDEX_NAME AS INDEX_NAME,

                       WM_CONCAT(CL.COLUMN_NAME) AS IX_CL

                  FROM DBA_IND_COLUMNS CL

                 WHERE (CL.INDEX_NAME LIKE 'IX%')

                   AND CL.TABLE_OWNER LIKE 'BI%'

                 GROUP BY CL.TABLE_OWNER, CL.TABLE_NAME, CL.INDEX_NAME) C

         WHERE I.OWNER = C.TABLE_OWNER

           AND I.TABLE_NAME = C.TABLE_NAME

           AND I.INDEX_NAME = C.INDEX_NAME

           AND I.STATUS<>'VALID') T

 WHERE T.TABLE_OWNER LIKE 'BI%'

 UNION ALL

 --主键索引高效重建(停用主键-->删除主键索引-->创建唯一索引-->将唯一索引启用为主键)

 SELECT 'ALTER TABLE ' || T.TABLE_OWNER || '.' || TABLE_NAME ||

' DROP CONSTRAINT ' || INDEX_NAME || ' CASCADE;' || CHR(10) ||

'DROP INDEX ' || T.TABLE_OWNER || '.' || INDEX_NAME || ';' ||

CHR(10) ||

       'CREATE UNIQUE INDEX ' || T.TABLE_OWNER || '.' || INDEX_NAME ||

' ON ' || T.TABLE_OWNER || '.' || TABLE_NAME || ' (' || T.IX_CL || ') ' ||

' TABLESPACE ' || T.TABLE_OWNER ||

' local NOLOGGING PARALLEL 16;' || CHR(10) || 'ALTER TABLE ' ||

T.TABLE_OWNER || '.' || TABLE_NAME || ' ADD CONSTRAINT ' ||

       INDEX_NAME || ' PRIMARY KEY (' || T.IX_CL || ');'

  FROM (SELECT I.TABLE_OWNER, I.TABLE_NAME, I.INDEX_NAME, C.IX_CL AS IX_CL

          FROM DBA_INDEXES I,

               (SELECT CL.TABLE_OWNER AS TABLE_OWNER,

                       CL.TABLE_NAME AS TABLE_NAME,

                       CL.INDEX_NAME AS INDEX_NAME,

                       WM_CONCAT(CL.COLUMN_NAME) AS IX_CL

                  FROM DBA_IND_COLUMNS CL

                 WHERE (CL.INDEX_NAME LIKE 'PK%')

                   AND CL.TABLE_OWNER LIKE 'BI%'

                 GROUP BY CL.TABLE_OWNER, CL.TABLE_NAME, CL.INDEX_NAME) C

         WHERE I.OWNER = C.TABLE_OWNER

           AND I.TABLE_NAME = C.TABLE_NAME

           AND I.INDEX_NAME = C.INDEX_NAME

       AND I.STATUS<>'VALID') T

 WHERE T.TABLE_OWNER LIKE 'BIODB';

 

其它实践例子,如下--------------------------------------------------------------------

--创建非分区表
create table test_partition_01( number_1 number, number_2 number, string_1 varchar2(10), string_2 varchar2(20) ); -- 写入数据 insert into test_partition_01(number_1, number_2, string_1, string_2) select dbms_random.random() as number_1, round(dbms_random.value(0, 100000)) as number_2, dbms_random.string(opt => 'A', len => 1) as String_1, dbms_random.string(opt => 'p', len => 10) as String_2 from dual connect by rownum < 100000; commit;
--试图创建本地分区索引 报错 CREATE INDEX ix_test_partition_01_1 ON test_partition_01(number_1) local (PARTITION p1, PARTITION p2, PARTITION p3);---ORA-14016 ---创建普通索引 CREATE INDEX ix_test_partition_01_1 ON test_partition_01(number_1); --创建 全局分区索引 CREATE INDEX ix_test_partition_01_2 ON test_partition_01(number_2) GLOBAL PARTITION BY RANGE (number_2) (PARTITION p1 VALUES LESS THAN (10000), PARTITION p2 VALUES LESS THAN (55000), PARTITION p3 VALUES LESS THAN (MAXVALUE)); 结论:非分区表可以创建普通索引和全局分区索引 不能创建本地分区索引。 ------------------------------------------------------------------------------- create table test_partiton_02( number_1 number, number_2 number, string_1 varchar2(10), string_2 varchar2(20) ) partition by range(number_2) ( partition p1 values less than (10000), partition p2 values less than (20000), partition p3 values less than (50000), partition p4 values less than (70000), partition p5 values less than (maxvalue) );
--试图创建本地分区索引 CREATE INDEX ix_test_partiton_02_1 ON test_partiton_02(number_1) local (PARTITION p1, PARTITION p2, PARTITION p3);--ora-14024 索引的分区数必须等于基础表的分区数 --创建本地分区索引 CREATE INDEX ix_test_partiton_02_1 ON test_partiton_02(number_1) local (PARTITION p1, PARTITION p2, PARTITION p3, PARTITION p4, PARTITION p5 );--drop index ix_test_partiton_02_1; CREATE INDEX ix_test_partiton_02_1 ON test_partiton_02(number_1) local; --和上面的创建方式等效 --drop index ix_test_partiton_02_1; CREATE INDEX ix_test_partiton_02_1 ON test_partiton_02(number_1) ; --默认创建的是 非分区索引,分区索引才分 全局索引还是本地索引 ; ---创建全局分区索引 CREATE INDEX ix_test_partiton_02_2 ON test_partiton_02(number_2) GLOBAL PARTITION BY RANGE (number_2) (PARTITION p1 VALUES LESS THAN (10000), PARTITION p2 VALUES LESS THAN (55000), PARTITION p3 VALUES LESS THAN (MAXVALUE)); ---写入测试数据 insert into test_partiton_02(number_1, number_2, string_1, string_2) select dbms_random.random() as number_1, round(dbms_random.value(0, 100000)) as number_2, dbms_random.string(opt => 'A', len => 1) as String_1, dbms_random.string(opt => 'p', len => 10) as String_2 from dual connect by rownum < 100001; commit; --分析表 analyze table test_partiton_02 compute statistics; --查看 普通索引是否可用 select * from user_indexes t where t.INDEX_NAME in('IX_TEST_PARTITON_02_1','IX_TEST_PARTITON_02_2','IX_TEST_PARTITON_02_3'); ---查看分区索引是否 可用 select * from user_ind_partitions t where t.INDEX_NAME in('IX_TEST_PARTITON_02_1','IX_TEST_PARTITON_02_2','IX_TEST_PARTITON_02_3'); ---改变分区,查看普通索引和分区索引是否可用 -- 必须调整最后一个分区的大小,所以如果最后一个分区指定了最大值 必须先删除,再添加 alter table test_partiton_02 add partition p6 values less than (90000); alter table test_partiton_02 drop partition p5; --- 结论 改变分区 普通索引和全局分区索引都会失效 只有本地分区索引好使 ,所以都带上本地分区索引
PS:最好用以下语句查看索引的定义语句 select dbms_metadata.get_ddl(object_type => 'INDEX', name => 'IX_TEST_PARTITON_02_1') FROM DUAL; select dbms_metadata.get_ddl(object_type => 'INDEX', name => 'IX_TEST_PARTITON_02_2') FROM DUAL;
--- 发现了 plsql developer Version 11.0.5.1790 (64 bit) 的一个 bug 在用View 查看 DDL时 没有反应真实的情况

下面简单测试一下:

1. 创建一个Range分区表:

CREATE TABLE DFMS.TEST04

PARTITION BY RANGE(OBJECT_ID)

(

  PARTITION P1 VALUES LESS THAN (2000)

    TABLESPACE LOG_DATA,

  PARTITION P2 VALUES LESS THAN (8000)

    TABLESPACE LOG_DATA,

  PARTITION P3 VALUES LESS THAN (20000)

    TABLESPACE LOG_DATA,

  PARTITION P4 VALUES LESS THAN (40000)

    TABLESPACE LOG_DATA,

  PARTITION PMAX VALUES LESS THAN (MAXVALUE)

    TABLESPACE LOG_DATA

)

AS

SELECT * FROM DBA_OBJECTS ;

2. 建立一个PK, 同时生成global index:

       alter table DFMS.TEST04 add constraint pk_id primary key(object_id);

   建立一个local index :

       CREATE INDEX DFMS.IDX1_TEST04 ON DFMS.TEST04 (OBJECT_NAME)  LOCAL ;

3. 我们通过dba_indexes视图查看global index的状态发现是valid :

       select index_name, status, last_analyzed,partitioned from dba_indexes where index_name='PK_ID' ;

本地索引local index通过dba_indexes查看的状态是N/A, 需要通过dba_ind_partitions来查看,可以看到每个索引分区都是USABLE状态。而通过DBA_PART_INDEXES可以看到这个本地分区索引的整体状态。

select * from dba_ind_partitions where index_name='IDX1_TEST04' ;

select * from dba_indexes where index_name='IDX1_TEST04' ;

select * from DBA_PART_INDEXES where index_name='IDX1_TEST04' ;

4. 因为存在maxvalue,我们先测试split对全局及本地索引的影响 .

4.1 新分区中都有数据的情况

alter table test04 split partition pmax at (80000) into

(partition p5 tablespace log_data ,

 partition pmax  tablespace log_data);

我们从table的脚本可以看出pmax被分成p5和pmax两部分:

....

  PARTITION P5 VALUES LESS THAN (80000),

  PARTITION PMAX VALUES LESS THAN (MAXVALUE) ;

.....

显然由于select max(object_id) from TEST04 的行数是101769,split后旧分区中符合less than 80000的留在了第一个分区p5,其他的都存在了第二个分区(新的pmax分区)。

我们查询global index及local index的状态:

select index_name, status, last_analyzed,partitioned

from dba_indexes where index_name='PK_ID' ;

 

这里显然触发了数据的移动,global index索引状态变成UNUSABLE.

select * from dba_ind_partitions where index_name='IDX1_TEST04' ;

因为新split出来的分区(这里指p5)中有数据,原pmax中的数据被拆分到新分区p5及新的pmax中,发现p1,p2,p3,p4 对应的本地索引仍然是USABLE,而新的p5及新pmax对应的本地索引都是UNUSABLE.

OK, 我们对global index及p5,pmax对应的本地分区索引进行rebuild :

alter index PK_ID rebuild online;

 

然后查询发现global index变成valid :

select index_name, status, last_analyzed,partitioned

from dba_indexes where index_name='PK_ID' ;

 

alter index IDX1_TEST04 rebuild partition p5 online;

alter index IDX1_TEST04 rebuild partition pmax online; 

执行之后查询:

select * from dba_ind_partitions where index_name='IDX1_TEST04' ;

可以看到两个索引分区p5及pmax状态都变成USABLE .

4.2 新分区中有一个没有数据

TEST04 的行数是101769,那么我们将p6新分区设置为110000,那么pmax分区显然就没有数据了。(新分区包含了所有PMAX分区的数据)

alter table test04 split partition pmax at (110000) into      ----这里注意一下,新加一个分区,但数据算没有移动的

(partition p6 tablespace log_data , partition pmax  tablespace log_data);

查看global index及local index可以看到全局索引及每个本地索引分区都是USABLE, 这是因为没有触发数据移动。

select index_name, status, last_analyzed,partitioned from dba_indexes where index_name='PK_ID' ;

select * from dba_ind_partitions where index_name='IDX1_TEST04' ;

备注:在split pmax分区时新的分区名称可以随便起(不一定含有pmax),比如上面的可以使用p6,p7, 只是p6会遵循less than 110000, 而第二个分区p7仍然是less than maxvalue.

 还有因为这里是表空间没有变化,如果非空的分区存储属性和原来的存储属性不一样,也会发生数据移动,也会导致索引失效。

 

5. 测试drop partition对全局及本地索引的影响。

对test04表的最后一个没有数据的pmax分区进行删除动作。

alter table test04 drop partition pmax ;

 

因为删除的分区没有数据,所以不涉及数据变化,所以对全局及本地所以都没有影响。

 

假设我们要删除有数据的部分,既不保留分区也不保留数据,那么本地索引不会受到影响,global index会失效。

alter table test04 drop partition p6 ;

查询

select * from dba_indexes where index_name='PK_ID' ;

全局索引失效,状态变成UNUSABLE .

select * from dba_ind_partitions where index_name='IDX1_TEST04' ;

本地索引(其他分区)状态不变,为USABLE .

6. 测试add partition 对全局索引和本地索引的影响。

alter table test04 add partition p6  values less than (120000) ;

查询状态:

select * from dba_indexes where index_name='PK_ID' ;

select * from dba_ind_partitions where index_name='IDX1_TEST04' ;

发现Range分区,加入分区对于全局及本地索引都没有影响。

同样测试list分区,也可以知道加入分区对于全局及本地索引都没有影响。这主要是因为没有触发数据的移动。

6.1 对于Hash分区,由于add parittion会发生数据分布平衡的I/O操作,数据会发生移动,所以本地分区索引及全局索引都会置为UNUSABLE, 需rebuild.下面做简单测试:

CREATE TABLE DFMS.TEST05

PARTITION BY HASH (OBJECT_ID)

PARTITIONS 8

STORE IN (LOG_DATA)

AS SELECT * FROM DBA_OBJECTS ;

加入global及local index .

alter table DFMS.TEST05 add constraint pk_test05_id primary key(object_id);

CREATE INDEX DFMS.IDX1_TEST05 ON DFMS.TEST05 (OBJECT_NAME)  LOCAL ;

加入新分区:

alter table test05 add partition ;

查询

select * from dba_indexes where index_name='PK_TEST05_ID' ;

select * from dba_ind_partitions where index_name='IDX1_TEST05' ;

发现global index是UNUSABLE状态,本地分区索引中的第一个和最后一个分区的本地分区索引是UNUSABLE状态,其它是USABLE. 显然因为数据从第一个分区被拆分到了新的hasn分区,所以这两个分区中的数据发生了移动,导致了本地分区索引的失效,因为有数据行的移动,当然global index也变成了失效状态(UNUSABLE) . 

SQL> select index_owner,index_name,STATUS from dba_ind_partitions where index_name='IDX1_TEST05';

INDEX_OWNER                    INDEX_NAME                     STATUS

------------------------------ ------------------------------ --------

SYS                            IDX1_TEST05                    UNUSABLE

SYS                            IDX1_TEST05                    USABLE

SYS                            IDX1_TEST05                    UNUSABLE

 

7. 测试truncate partition 对全局索引和本地索引的影响。

Truncate partition 就像truncate table 一样,直接从头部截断数据。在不指定update indexes 子句的情况下,truncate partition 也会造成分区所在表的global 索引失效。语法非常简单:

alter table tbname truncate partition/subpartition ptname;

alter table test04 truncate partition p6 ;

查询

select * from dba_indexes where index_name='PK_ID' ;

select * from dba_ind_partitions where index_name='IDX1_TEST04' ;

发现global index索引失效,本地分区索引状态都是USABLE .

8. 其他操作如 Merge Partitions,Exchange Partitions及coalesce partitions等较少使用,这里不做测试。

其实总之,如果发生数据移动,那么索引肯定是需要注意的。


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM