Oracle分區表刪除分區引起錯誤ORA-01502: 索引或這類索引的分區處於不可用狀態

(一)問題:數據庫

最近在作Oracle數據清理,在對分區表進行數據清理時,採用的方法是drop partition,刪除的過程當中,沒有遇到任何問題,大概過了10分鐘,開發人員反饋部分分區表上的業務失敗。具體錯誤爲:less

ORA-01502錯誤:索引或這類索引的分區處於不可用狀態(英文:ora-01502:index 'schema.index_name' or partition of such index is in unusable state)。測試

(二)緣由分析ui

查看出現問題的分區表,均有一個共同點:表上以「pk_」開頭的索引爲unusable狀態,以「pk_」開頭的索引是隨建立主鍵約束而建立的。當用戶在建立主鍵約束或惟一性約束的時候,會在相應的列上建立惟一性索引spa

通過查證,發現是在刪除分區的時候,致使分區表上的惟一性全局索引爲不可用狀態,致使新的數據沒法正常插入,從而引起了該錯誤。設計

是否是索引不可用會致使DML操做失敗呢?通過驗證,發現如下特色:code

1.對於非惟一性索引,若是索引不可用,是不會影響到到DML操做的;對象

2.對於惟一性索引,若是索引不可用,在進行DML操做時,會觸發ORA-01502錯誤;blog

 

這裏記錄一下哪些操做會致使索引失效:索引

image

                                                              圖1.索引失效緣由總結

(三)解決方案

(3.1)瞭解惟一性索引

在解決問題以前,咱們來分析一下,哪些行爲會建立惟一性索引(3種):

--直接建立惟一性索引。

     語法爲:CREATE UNIQUE INDEX index_name on table_name(col1,col2,…);

--建立主鍵約束時自動建立惟一性索引。

     語法爲:ALTER TABLE table_name ADD CONSTRAINT constraint_name PRIMARY KEY(col1,col2,..);

--建立惟一性約束時自動建立惟一性索引。

     語法爲:ALTER TABLE table_name ADD CONSTRAINT constraint_name UNIQUE(col1,col2,…);

 

這裏,我總結了3套方案來對應ORA-01502問題。

(3.2)方案一:刪除惟一性索引

與業務方面溝通,確認惟一性索引是否能夠刪除,若是能夠,直接刪除索引,刪除語法爲:

SQL> DROP INDEX schema.index_name;

對於由主鍵約束或惟一性約束建立的惟一性索引,不能直接刪除

SQL> drop index lijiaman.sale_pk;
drop index lijiaman.sale_pk

ORA-02429: cannot drop index used for enforcement of unique/primary key

正確的方法是刪除相應的約束。

SQL> alter table sales drop constraint sale_pk;

Table altered

小結:該方法簡單粗暴,前提是在約束或索引在業務方面能夠刪除的狀況下才能使用。

(3.3)方案二:重建惟一性索引(針對非分區表)

語法爲:

SQL> ALTER INDEX [schema.]index_name REBUILD [ONLINE] [TABLESPACE tablespace name]

小結:該方法可使索引可用。但對於分區表而言,依然存在問題:在下一次刪除分區後,索引狀態又會變爲不可用。

(3.4)方案三:刪除不可用的索引,建立惟一性分區索引(針對分區表)

建立惟一性分區索引:

SQL> CREATE UNIQUE INDEX index_name on [schema.]table(col1,col2,...);

對於主鍵約束、惟一性約束,可使用如下語法添加惟一性局部分區索引:

SQL> ALTER TABLE [schema.]table_name ADD CONSTRAINT constarint [PRIMARY KEY | UNIQUE](col1,col2)
     USING INDEX LOCAL TABLESPACE tablespace_name;

小結:該方法能夠有效解決分區表因刪除分區致使的索引不可用問題。

 

 

附錄:模擬實驗

(1)首先模擬生產狀況,建立一張表:

create table sales
(
  prod_id number,
  cust_id number,
  time_id date,
  quantity_sold number(3)
)
partition by range(time_id)
(
  partition sales_q1_2017 values less than(to_date('1-4-2017','dd-mm-yyyy')) ,
  partition sales_q2_2017 values less than(to_date('1-7-2017','dd-mm-yyyy')) ,
  partition sales_q3_2017 values less than(to_date('1-10-2017','dd-mm-yyyy')) ,
  partition sales_q4_2017 values less than(to_date('1-1-2018','dd-mm-yyyy'))
);

插入數據,確保每一個分區都有數據

insert into sales(prod_id,cust_id,time_id,quantity_sold)values(1,11,to_date('2017-02-01','yyyy-mm-dd'),103);
insert into sales(prod_id,cust_id,time_id,quantity_sold)values(2,12,to_date('2017-06-01','yyyy-mm-dd'),103);
insert into sales(prod_id,cust_id,time_id,quantity_sold)values(3,14,to_date('2017-08-01','yyyy-mm-dd'),103);
insert into sales(prod_id,cust_id,time_id,quantity_sold)values(4,14,to_date('2017-12-01','yyyy-mm-dd'),103);

檢查一下數據庫的數據信息

SQL> select * from sales;        --查看整個分區表的數據

   PROD_ID    CUST_ID TIME_ID     QUANTITY_SOLD
---------- ---------- ----------- -------------
         1         11 2017/2/1              103
         2         12 2017/6/1              103
         3         14 2017/8/1              103
         4         14 2017/12/1             103

SQL> select * from sales partition(sales_q1_2017);      --查看分區「sales_q1_2017」的數據

   PROD_ID    CUST_ID TIME_ID     QUANTITY_SOLD
---------- ---------- ----------- -------------
         1         11 2017/2/1              103

(2)因爲出現ORA-01502問題時,與表相關的對象只有主鍵約束和索引。因此,我在表上建立了索引和約束,並確認了全部索引可用

alter table sales add constraint sale_pk primary key(time_id,cust_id);  --建立主鍵約束
create index inx_sales_1 on sales(cust_id);  --建立普通(全局)索引
create index inx_sales_2 on sales(time_id) local;  --建立局部分區索引

確認索引狀態:

SQL> select   owner,table_name,index_name,status
  2  from     dba_indexes
  3  where    owner = 'LIJIAMAN'
  4  and      table_name = 'SALES';

OWNER                          TABLE_NAME                     INDEX_NAME                     STATUS
------------------------------ ------------------------------ ------------------------------ --------
LIJIAMAN                       SALES                          INX_SALES_2                    N/A
LIJIAMAN                       SALES                          SALE_PK                        VALID
LIJIAMAN                       SALES                          INX_SALES_1                    VALID

對於索引「SALES_PK」和「INX_SALES_1」,索引狀態爲可用,那」INX_SALES_2「這個索引狀態爲」N/A「,這又是怎麼回事麼?通過查找資料,確認索引共有四種狀態:

  • N/A         :說明這個是分區索引須要查user_ind_partitions或者user_ind_subpartitions來肯定每一個分區是否可用;
  • VAILD      :說明這個索引可用;
  • UNUSABLE:說明這個索引不可用;
  • USABLE    :說明這個索引的分區是可用的。

咱們再去查看數據字典DBA_IND_PARTITIONS,確認」INX_SALES_2」的狀態,索引可用。

SQL> SELECT INDEX_OWNER,INDEX_NAME,PARTITION_NAME,STATUS FROM DBA_IND_PARTITIONS I WHERE I.INDEX_OWNER = 'LIJIAMAN' AND I.INDEX_NAME = 'INX_SALES_2';

INDEX_OWNER                    INDEX_NAME                     PARTITION_NAME                 STATUS
------------------------------ ------------------------------ ------------------------------ --------
LIJIAMAN                       INX_SALES_2                    SALES_Q1_2017                  USABLE
LIJIAMAN                       INX_SALES_2                    SALES_Q2_2017                  USABLE
LIJIAMAN                       INX_SALES_2                    SALES_Q3_2017                  USABLE
LIJIAMAN                       INX_SALES_2                    SALES_Q4_2017                  USABLE

確認主鍵約束的狀態,確承認用

SQL> select   owner,table_name,constraint_name,constraint_type,status,deferrable,deferred,validated
  2  from     dba_constraints
  3  where    owner = 'LIJIAMAN'
  4  and      table_name = 'SALES';

OWNER     TABLE_NAME   CONSTRAINT_NAME   CONSTRAINT_TYPE STATUS   DEFERRABLE     DEFERRED  VALIDATED
--------- ------------ ----------------- --------------- -------- -------------- --------- -------------
LIJIAMAN  SALES        SALE_PK           P               ENABLED  NOT DEFERRABLE IMMEDIATE VALIDATED

(3)接下來,咱們模擬數據清理,刪除分區」sales_q1_2017「

SQL> alter table sales drop partition sales_q1_2017 ;

Table altered

查看分區表的數據,能夠看到,分區」sales_q1_2017「的數據已經隨着分區被刪除

SQL> select * from sales;

   PROD_ID    CUST_ID TIME_ID     QUANTITY_SOLD
---------- ---------- ----------- -------------
         2         12 2017/6/1              103
         3         14 2017/8/1              103
         4         14 2017/12/1             103

 

==================轉折點==========================

 

(4)此時,咱們模擬正常的業務交易,發現以下狀況

    --對於insert操做,沒法完成,報ORA-01502錯誤;

    --對於delete操做,沒法完成,報ORA-01502錯誤;

    --對於update操做,若是不涉及到主鍵相關的列,則能夠執行成功,若是涉及到主鍵列,報ORA-01502錯誤;

--數據插入測試,發現沒法插入數據
SQL> insert into sales(prod_id,cust_id,time_id,quantity_sold)values(5,15,to_date('2017-8-01','yyyy-mm-dd'),103);
insert into sales(prod_id,cust_id,time_id,quantity_sold)values(5,15,to_date('2017-8-01','yyyy-mm-dd'),103)
ORA-01502: index 'LIJIAMAN.SALE_PK' or partition of such index is in unusable state

--數據刪除測試,發現沒法刪除數據
SQL> delete from sales where prod_id = 3 ;
delete from sales where prod_id = 3
ORA-01502: index 'LIJIAMAN.SALE_PK' or partition of such index is in unusable state

--數據跟新測試,測試3次,發現涉及到與主鍵相關的列,就會更新失敗,其餘狀況更新成功
SQL> update sales set QUANTITY_SOLD = 105 where PROD_ID = 2;
1 row updated
SQL> commit;
Commit complete

SQL> update sales set QUANTITY_SOLD = 105 where cust_id = 12;
1 row updated
SQL> commit;
Commit complete

SQL> update sales set cust_id = 15 where PROD_ID = 2;
update sales set cust_id = 15 where PROD_ID = 2
ORA-01502: index 'LIJIAMAN.SALE_PK' or partition of such index is in unusable state

再次確認,索引的狀態,能夠看到,普通索引狀態已經轉變爲不可用,而局部分區索引狀態未發生改變。

SQL> select   owner,table_name,index_name,status
  2  from     dba_indexes
  3  where    owner = 'LIJIAMAN'
  4  and      table_name = 'SALES';

OWNER                          TABLE_NAME                     INDEX_NAME                     STATUS
------------------------------ ------------------------------ ------------------------------ --------
LIJIAMAN                       SALES                          INX_SALES_2                    N/A
LIJIAMAN                       SALES                          SALE_PK                        UNUSABLE
LIJIAMAN                       SALES                          INX_SALES_1                    UNUSABLE


SQL> SELECT INDEX_OWNER,INDEX_NAME,PARTITION_NAME,STATUS FROM DBA_IND_PARTITIONS I WHERE I.INDEX_OWNER = 'LIJIAMAN' AND I.INDEX_NAME = 'INX_SALES_2';

INDEX_OWNER                    INDEX_NAME                     PARTITION_NAME                 STATUS
------------------------------ ------------------------------ ------------------------------ --------
LIJIAMAN                       INX_SALES_2                    SALES_Q2_2017                  USABLE
LIJIAMAN                       INX_SALES_2                    SALES_Q3_2017                  USABLE
LIJIAMAN                       INX_SALES_2                    SALES_Q4_2017                  USABLE

主鍵約束狀態也爲發生改變

OWNER      TABLE_NAME   CONSTRAINT_NAME  CONSTRAINT_TYPE STATUS   DEFERRABLE     DEFERRED  VALIDATED
---------- ------------ ---------------- --------------- -------- -------------- --------- -------------
LIJIAMAN   SALES        SALE_PK          P               ENABLED  NOT DEFERRABLE IMMEDIATE VALIDATED

這裏,咱們對deop分區先後表的信息對比作一個小結

  刪除分區前 刪除分區後
查詢(select) 正常 正常
插入(insert) 正常 沒法插入
刪除(delete) 正常 沒法刪除
更新(update) 正常 設計到主鍵相關的列,更新失敗,其餘狀況更新成功
約束狀態(constraint) 可用 可用
索引狀態(index) 所有可用 1.分區索引可用
2.主鍵約束上的惟一性索引不可用
3.普通索引不可用

經過對比,咱們能夠推測,索引不可用致使了沒法正常DML操做。那麼究竟是哪一個索引致使的問題呢?

(5)首先測試普通索引,先重建索引INX_SALES_1

SQL> alter index lijiaman.inx_sales_1 rebuild;

Index altered

SQL> select   owner,table_name,index_name,status
  2  from     dba_indexes
  3  where    owner = 'LIJIAMAN'
  4  and      table_name = 'SALES';

OWNER                          TABLE_NAME                     INDEX_NAME                     STATUS
------------------------------ ------------------------------ ------------------------------ --------
LIJIAMAN                       SALES                          INX_SALES_2                    N/A
LIJIAMAN                       SALES                          SALE_PK                        UNUSABLE
LIJIAMAN                       SALES                          INX_SALES_1                    VALID

繼續模擬DML交易,狀況與刪除分區後的DML結果相同,能夠確認,普通索引不可用並不會引發DML操做失敗

--數據依然沒法插入
SQL> insert into sales(prod_id,cust_id,time_id,quantity_sold)values(5,15,to_date('2017-8-01','yyyy-mm-dd'),103);
insert into sales(prod_id,cust_id,time_id,quantity_sold)values(5,15,to_date('2017-8-01','yyyy-mm-dd'),103)
ORA-01502: index 'LIJIAMAN.SALE_PK' or partition of such index is in unusable state

--數據沒法刪除
SQL> delete from sales where prod_id = 3 ;
delete from sales where prod_id = 3
ORA-01502: index 'LIJIAMAN.SALE_PK' or partition of such index is in unusable state

--若是沒有更新到逐漸相關列,能夠更新數據,不然不行
SQL> update sales set QUANTITY_SOLD = 105 where PROD_ID = 2;
1 row updated

SQL> update sales set QUANTITY_SOLD = 105 where cust_id = 12;
1 row updated

SQL> update sales set cust_id = 15 where PROD_ID = 2;
update sales set cust_id = 15 where PROD_ID = 2
ORA-01502: index 'LIJIAMAN.SALE_PK' or partition of such index is in unusable state

(6)接着重建惟一性索引」SALE_PK」

SQL> alter index lijiaman.sale_pk rebuild;

Index altered

對SALES表進行DML操做,能夠正常進行

SQL> insert into sales(prod_id,cust_id,time_id,quantity_sold)values(5,15,to_date('2017-8-01','yyyy-mm-dd'),103);
1 row inserted

SQL> delete from sales where prod_id = 3 ;
1 row deleted

SQL> update sales set QUANTITY_SOLD = 105 where PROD_ID = 2;
1 row updated

SQL> update sales set QUANTITY_SOLD = 105 where cust_id = 12;
1 row updated

SQL> update sales set cust_id = 15 where PROD_ID = 2;
1 row updated

SQL> commit;
Commit complete

至此,咱們能夠大膽猜想:惟一性索引致使的ORA-01502問題。因爲咱們在建立索引的時候,並未直接建立惟一性索引,而是在建立主鍵約束的時候自動建立的惟一性索引,那麼究竟是主鍵約束的問題,仍是惟一性索引的問題?根據上面刪除分區先後約束狀態相同,而索引狀態不一樣,我以爲是索引的問題繼續求證。咱們新建一個表,在上面直接建立惟一性索引,不建立任何約束。

建立表test01,錄入數據

SQL> select * from test01;

        ID NAME                        AGE
---------- -------------------- ----------
         1 lijiaman             
         2 gegeman                      25
         3 xiaoman                      26
         4 Lijiaman                     25

在「ID」列建立惟一性索引

SQL> create unique index inx_test01 on test01(id);

Index created


SQL> select table_owner,index_name,index_type,uniqueness,status
  2  from   user_indexes
  3  where  table_name = 'TEST01';

TABLE_OWNER                    INDEX_NAME                     INDEX_TYPE                  UNIQUENESS STATUS
------------------------------ ------------------------------ --------------------------- ---------- --------
LIJIAMAN                       INX_TEST01                     NORMAL                      UNIQUE     VALID

插入數據,沒有異常

SQL> insert into test01 values(5,'bokeyuan',22);

1 row inserted

接着將索引置爲不可用狀態,而後往表裏面插入數據,出現了01502錯誤;

SQL> alter index lijiaman.inx_test01 unusable;     --將索引置爲不可用狀態
Index altered

SQL> select table_owner,index_name,index_type,uniqueness,status
  2  from   user_indexes
  3  where  table_name = 'TEST01';

TABLE_OWNER                    INDEX_NAME                     INDEX_TYPE                  UNIQUENESS STATUS
------------------------------ ------------------------------ --------------------------- ---------- --------
LIJIAMAN                       INX_TEST01                     NORMAL                      UNIQUE     UNUSABLE


SQL> insert into test01 values(5,'bokeyuan',25);    --插入數據,發生ORA-01502錯誤
insert into test01 values(5,'bokeyuan',25)
ORA-01502: index 'LIJIAMAN.INX_TEST01' or partition of such index is in unusable state

至此能夠明確的說:ORA-01512錯誤是因爲惟一性索引失效致使的。

如何解決這個問題,前面給出了3種方案,只要選擇其中一種便可,再也不模擬。

相關文章
相關標籤/搜索