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