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种方案,只要选择其中一种便可,再也不模拟。

相关文章
相关标签/搜索