• Oracle分区表删除分区引发错误ORA-01502: 索引或这类索引的分区处于不可用状态


    (一)问题:

    最近在做Oracle数据清理,在对分区表进行数据清理时,采用的方法是drop partition,删除的过程中,没有遇到任何问题,大概过了10分钟,开发人员反馈部分分区表上的业务失败。具体错误为:

    ORA-01502错误:索引或这类索引的分区处于不可用状态(英文:ora-01502:index 'schema.index_name' or partition of such index is in unusable state)。

    (二)原因分析

    查看出现问题的分区表,均有一个共同点:表上以“pk_”开头的索引为unusable状态,以“pk_”开头的索引是随创建主键约束而创建的。当用户在创建主键约束或唯一性约束的时候,会在相应的列上创建唯一性索引

    经过查证,发现是在删除分区的时候,导致分区表上的唯一性全局索引为不可用状态,导致新的数据无法正常插入,从而引发了该错误。

    是不是索引不可用会导致DML操作失败呢?经过验证,发现以下特点:

    1.对于非唯一性索引,如果索引不可用,是不会影响到到DML操作的;

    2.对于唯一性索引,如果索引不可用,在进行DML操作时,会触发ORA-01502错误;

    这里记录一下哪些操作会导致索引失效:

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

  • 相关阅读:
    躺着的人
    (转载)CentOS查看系统信息|CentOS查看命令
    (转载)重新介绍 JavaScript(JS 教程)
    (转载)Java 容器 & 泛型:四、Colletions.sort 和 Arrays.sort 的算法
    (转载)Java 容器 & 泛型:三、HashSet,TreeSet 和 LinkedHashSet比较
    (转载)Java 容器 & 泛型:二、ArrayList 、LinkedList和Vector比较
    (转载)Java 容器 & 泛型:一、认识容器
    (转载)Python IDLE reload(sys)后无法正常执行命令的原因
    jmete 取配置文件的行数(二)
    jmete 取配置文件的行数(一)
  • 原文地址:https://www.cnblogs.com/lijiaman/p/9277149.html
Copyright © 2020-2023  润新知