zoukankan      html  css  js  c++  java
  • 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种方案,只要选择其中一种即可,不再模拟。

  • 相关阅读:
    解决:Could not resolve archetype org.apache.maven.archetypes
    Spring MVC配置MyBatis输出SQL
    Spring集成MyBatis 通用Mapper以及 pagehelper分页插件
    关于SpringMVC或Struts2接受参数接收不到的原因
    配置quartz启动时就执行一次
    ajaxFileUpload进行文件上传时,总是进入error
    spring mvc注入配置文件里的属性
    java中将一个文件夹下所有的文件压缩成一个文件
    flume failed to start agent because dependencies were not found in classpath
    ubuntu不能安装pip unable to install pip in unbuntu
  • 原文地址:https://www.cnblogs.com/lijiaman/p/9277149.html
Copyright © 2011-2022 走看看