暂无图片
暂无图片
5
暂无图片
暂无图片
暂无图片

删除分区如何不让全局索引失效?

3025

记得上次ACOUG年会(《ACOUG年会感想》),请教杨长老问题的时候,谈到分区,如果执行分区删除的操作,就会导致全局索引失效,除了使用12c以上版本能避免这个问题外,指出另外一种解决的方式,表面看很巧妙,实则是对分区原理的深入理解。


我们先从实验,了解这个问题,首先创建分区表,他存在4个分区,每个分区中,都存在数据,

SQL> CREATE TABLE interval_sale
  2  ( prod_id        NUMBER(6)
  3  , cust_id        NUMBER
  4  , time_id        DATE
  5  )
  6  PARTITION BY RANGE (time_id)
  7  INTERVAL(NUMTOYMINTERVAL(1'YEAR'))
  8    ( PARTITION p0 VALUES LESS THAN (TO_DATE('1-1-2003''DD-MM-YYYY')),
  9      PARTITION p1 VALUES LESS THAN (TO_DATE('1-1-2004''DD-MM-YYYY')),
 10      PARTITION p2 VALUES LESS THAN (TO_DATE('1-1-2005''DD-MM-YYYY')),
 11      PARTITION p3 VALUES LESS THAN (TO_DATE('1-1-2006''DD-MM-YYYY')))
;

SQL> insert into interval_sale values(11, to_date('2002-01-01','yyyy-mm-dd'));
1 row created.

SQL> insert into interval_sale values(22, to_date('2003-01-01','yyyy-mm-dd'));
1 row created.

SQL> insert into interval_sale values(33, to_date('2004-01-01','yyyy-mm-dd'));
1 row created.

SQL> insert into interval_sale values(44, to_date('2005-01-01','yyyy-mm-dd'));
1 row created.

SQL> commit;
Commit complete.


创建全局索引,当前状态是VALID,

SQL> create index idx_01 on interval_sale(cust_id);
Index created.

SQL> select table_name, index_name, partitioned, status
  2  from user_indexes where table_name='INTERVAL_SALE';
TABLE_NAME     INDEX_NAME   PARTITIONED STATUS
--------------- --------------- ------------ --------
INTERVAL_SALE    IDX_01       NO VALID


删除第一个分区,
SQL> alter table interval_sale drop partition for (to_date('2002-01-01','yyyy-mm-dd'));
Table altered.


此时,看到这个全局索引是UNUSABLE的状态,和我们的设想是相同的,即删除分区,会导致全局索引的失效,

SQL> select table_name, index_name, status
  2  from user_indexes where table_name='INTERVAL_SALE';
TABLE_NAME     INDEX_NAME   STATUS
--------------- --------------- ----------
INTERVAL_SALE    IDX_01       UNUSABLE


结论告诉我们,删除分区,确实会导致全局索引的失效,我们从问题入手,为什么分区删除,会导致全局索引的失效?


我们知道,Oracle中索引是以B树的结构存储的,包括了索引键值、rowid信息,而且按照索引键值有序排列,当通过索引扫描需要回表的时候,能利用rowid直接定位到索引键值对应的数据块,这是最快的数据访问方式。当我们删除表中数据的时候,同时要删除他对应的索引,由于索引是有序排列的,如果要删除一条索引数据,他的组织结构,就需要调整,以保证正确的排列顺序,12c之前,因为某种原因,无法在删除分区的同时,对索引重新构建,所以此时索引的状态是失效的,与其是错的,宁可不让用,删除分区,需要手工rebuild重建索引才能让其生效,


我们换种思路,之所以全局索引的状态失效,根本问题就是索引对应的分区中数据被删除了,那么,如果不删除分区中的数据,索引结构无需任何调整,他的状态是不是就是正常的?


首先重建索引,让其生效,

SQL> alter index idx_01 rebuild online;
Index altered.

SQL> select table_name, index_name, status
  2  from user_indexes where table_name='INTERVAL_SALE';
TABLE_NAME     INDEX_NAME   STATUS
--------------- --------------- --------
INTERVAL_SALE    IDX_01       VALID


此时,通过delete删除即将删除的第二个分区的数据,

SQL> delete from interval_sale where time_id <= to_date('2003-01-01','yyyy-mm-dd');
1 row deleted.

SQL>
 commit;
Commit complete.


再次执行分区删除的操作,

SQL> alter table interval_sale drop partition for (to_date('2003-01-01','yyyy-mm-dd'));
Table altered.


此时,再看全局索引,他的状态正常,VALID,并未因为分区删除的操作,导致其失效,

SQL> select table_name, index_name, status
  2  from user_indexes where table_name='INTERVAL_SALE';
TABLE_NAME     INDEX_NAME   STATUS
--------------- --------------- --------
INTERVAL_SALE    IDX_01       VALID


通过以上实验,可以得到结论,如果待删除的分区中没有任何数据,执行分区删除,不会导致全局索引状态的失效。原因已经说了,因为分区删除时,不存在任何数据需要删除,意味着无需调整索引结构,所以全局索引的状态,就无需置为失效,这个算是对待分区删除避免全局索引失效的一种另类解决方案了。


通过这个问题,能让我体会到的,就是一个看着很简单的问题背后,其实蕴涵着丰富的知识,同时对待任何一个知识点,从原理层理解地越深入,找到问题的本质,就可以让你和真相更近,豁然开朗,这可能就需要日常的积累,碰到问题的时候,多问一句为什么,就可能让你大开眼界,这就是Oracle以及技术领域最吸引人的地方了。

文章转载自bisal的个人杂货铺,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论