《Oracle编程艺术》学习笔记(39)-索引分区

对索引进行分区有两种方法:
1) 随表对索引完成相应的分区:这也称为局部分区索引(locally partitioned index)。
每个表分区都有一个索引分区,而且只索引该表分区。一个给定索引分区中的所有条目都指向一个表分区,表分区中的所有行都表示在一个索引分区中。
2)按区间对索引分区:这也称为全局分区索引(globally partitioned index)。
索引按区间分区或者按散列(10g之后)分区,一个索引分区可能指向任何(和所有)表分区。
由于全局索引只按区间或散列分区,如果希望有一个列表或组合分区索引,就必须使用局部索引。而局部索引会使用底层表相同的机制分区。

局部分区索引
使用LOCAL关键字创建局部分区索引。
局部分区索引使用和基表相同的分区来保存索引。
如果使用一个局部索引来保证惟一性约束(PRIMARY KEY或者UNIQUE),那么分区键必须包括在约束本身中。

tony@ORA11GR2> create table t (x int, y int, data varchar2(32))
  2  partition by range (x) (
  3    partition part_1 values less than (2) tablespace ts1,
  4    partition part_2 values less than (3) tablespace ts2
  5  );
Table created.

tony@ORA11GR2> create index t_idx_local on t (x, y) local;
Index created.

tony@ORA11GR2> alter table t add (primary key (x));
Table altered.

tony@ORA11GR2> select segment_name,segment_type,partition_name,tablespace_name
  2  from user_segments order by segment_name;

SEGMENT_NAME         SEGMENT_TYPE    PARTITION_ TABLESPACE
-------------------- --------------- ---------- ----------
T                    TABLE PARTITION PART_1     TS1
T                    TABLE PARTITION PART_2     TS2
T_IDX_LOCAL          INDEX PARTITION PART_1     TS1
T_IDX_LOCAL          INDEX PARTITION PART_2     TS2
...


tony@ORA11GR2> drop index t_idx_local;
drop index t_idx_local
           *
ERROR at line 1:
ORA-02429: cannot drop index used for enforcement of unique/primary key

需要注意,如果分区中有数据,对分区进行的合并/拆分操作会导致局部分区索引不可用。
可以操作之后重建索引,也可以在操作时使用update indexes子句。

tony@ORA11GR2> select index_name, partition_name, status from user_ind_partitions;
INDEX_NAME      PARTITION_ STATUS
--------------- ---------- ----------------
T_IDX_LOCAL     PART_2     USABLE
T_IDX_LOCAL     PART_1     USABLE

tony@ORA11GR2> alter table t merge partitions part_1, part_2
  2  into partition part_2 tablespace ts2;
Table altered.

tony@ORA11GR2> select index_name, partition_name, status from user_ind_partitions;
INDEX_NAME      PARTITION_ STATUS
--------------- ---------- ----------------
T_IDX_LOCAL     PART_2     UNUSABLE

tony@ORA11GR2> alter index t_idx_local rebuild partition part_2;
Index altered.

tony@ORA11GR2> select index_name, partition_name, status from user_ind_partitions;
INDEX_NAME      PARTITION_ STATUS
--------------- ---------- ----------------
T_IDX_LOCAL     PART_2     USABLE

tony@ORA11GR2> alter table t split partition part_2 at (2)
  2  into (partition part_1 tablespace ts1, partition part_2 tablespace ts2)
  3  update indexes;
Table altered.

tony@ORA11GR2> select index_name, partition_name, status from user_ind_partitions;
INDEX_NAME      PARTITION_ STATUS
--------------- ---------- ----------------
T_IDX_LOCAL     PART_1     USABLE
T_IDX_LOCAL     PART_2     USABLE


全局分区索引
使用GLOBAL关键字创建全局分区索引。
全局索引的索引键必须从该索引的分区键开始,即不论用什么属性对索引分区,这些属性都必须是索引键的前几列。

tony@ORA11GR2> create table orders (order_date date, order# int, data varchar2(32))
  2  partition by range (order_date) (
  3    partition part_1 values less than (to_date('2011/01/01','yyyy/mm/dd')) tablespace ts1,
  4    partition part_2 values less than (maxvalue) tablespace ts2
  5  );
Table created.

tony@ORA11GR2> create index orders_idx on orders (order#) global
  2  partition by range (order#) (
  3    partition part_idx_1 values less than (1000) tablespace ts1,
  4    partition part_idx_2 values less than (2000) tablespace ts2,
  5    partition part_idx_3 values less than (maxvalue) tablespace ts3
  6  );
Index created.

tony@ORA11GR2> alter table orders add constraint
  2  orders_pk primary key (order#);
Table altered.

Oracle会使用orders_idx来保证主键。

tony@ORA11GR2> drop index orders_idx;
drop index orders_idx
           *
ERROR at line 1:
ORA-02429: cannot drop index used for enforcement of unique/primary key

如果分区中有数据,增加,删除,合并,拆分分区会导致全局索引不可用。
可以操作之后重建索引,也可以在操作时使用update global indexes子句。

tony@ORA11GR2> alter table orders drop partition part_1
  2  update global indexes;
Table altered.

tony@ORA11GR2> select index_name, partition_name, status from user_ind_partitions;
INDEX_NAME      PARTITION_ STATUS
--------------- ---------- ----------------
ORDERS_IDX      PART_IDX_1 USABLE
ORDERS_IDX      PART_IDX_2 USABLE
ORDERS_IDX      PART_IDX_3 USABLE

滑动窗口和全局索引
在许多实现中,会随着时间的推移向数据仓库中增加数据,而最旧的数据会老化。
在很多时候,这个数据会按一个日期属性进行区间分区,所以最旧的数据多存储在一个分区中,新加载的数据很可能都存储在一个新分区中。
每次的加载过程涉及:
1)去除老数据:最旧的分区要么被删除,要么与一个空表交换(将最旧的分区变为一个表),从而允许对旧数据进行归档。
2)加载新数据并建立索引:将新数据加载到一个“工作”表中,建立索引并进行验证。
3)关联新数据:将数据所在的表会与分区表中的一个空分区交换,将表中的这些新加载的数据变成分区表中的一个分区。
下面模拟这个过程,建立ORDERS分区表,由2个分区,分别包含2009,2010年的数据,
ORDERS分区表上建立2个索引,一个是全局索引的,一个是局部分区索引。

tony@MYTEST2> create table orders (order_ts date, order# int, data varchar2(32))
  2  partition by range (order_ts) (
  3    partition part_2009 values less than (to_date('2010/01/01','yyyy/mm/dd')),
  4    partition part_2010 values less than (to_date('2011/01/01','yyyy/mm/dd'))
  5  );
Table created.

tony@MYTEST2> create index orders_idx_ts_local on orders (order_ts) local;
Index created.

tony@MYTEST2> create index orders_idx_id_global on orders (order#) global;
Index created.

tony@MYTEST2> insert into orders
  2    select to_date('2009/01/01','yyyy/mm/dd')+rownum, 20090000+rownum, null
  3    from dual connect by level < 365;
364 rows created.

tony@MYTEST2> insert into orders
  2    select to_date('2010/01/01','yyyy/mm/dd')+rownum, 20100000+rownum, null
  3    from dual connect by level < 365;
364 rows created.


现在正处于年末,我们想删除最旧的财政年度数据,并将其归档。
我们交换分区和表格,使分区变成一个满表,而将空表变成一个分区。这是一个简单的数据字典更新,瞬时就会完成,而不会发生大量的I/O。
然后将空的分区删除,将表格归档。

tony@MYTEST2> create table orders_2009 (order_ts date, order# int, data varchar2(32));
Table created.

tony@MYTEST2> create index orders_2009_idx_ts on orders_2009 (order_ts);
Index created.

tony@MYTEST2> alter table orders exchange partition part_2009
  2  with table orders_2009
  3  including indexes
  4  without validation;
Table altered.

tony@MYTEST2> select * from orders partition(part_2009);
no rows selected

tony@MYTEST2> select count(*) from orders_2009;
  COUNT(*)
----------
       364

tony@MYTEST2> alter table orders drop partition part_2009;
Table altered.


接下来“滑入”(即增加)新数据。
创建空的分区,将它和满表交换,这个操作也会很快完成。

tony@MYTEST2> create table orders_2011 (order_ts date, order# int, data varchar2(32));
Table created.

tony@MYTEST2> insert into orders_2011
  2    select to_date('2011/01/01','yyyy/mm/dd')+rownum, 20110000+rownum, null
  3    from dual connect by level < 365;
364 rows created.

tony@MYTEST2> create index orders_2011_idx_ts on orders_2011 (order_ts);
Index created.

tony@MYTEST2> alter table orders
  2  add partition part_2011 values less than (to_date('2012/01/01','yyyy/mm/dd'));
Table altered.

tony@MYTEST2> alter table orders exchange partition part_2011
  2  with table orders_2011
  3  including indexes
  4  without validation;
Table altered.

tony@MYTEST2> select count(*) from orders partition (part_2011);
  COUNT(*)
----------
       364


不过这个操作完成以后,全局索引是无效的。必须重建这个索引。

tony@MYTEST2> select index_name, status from user_indexes
  2  where table_name = 'ORDERS';
INDEX_NAME                     STATUS
------------------------------ --------
ORDERS_IDX_ID_GLOBAL           UNUSABLE
ORDERS_IDX_TS_LOCAL            N/A

tony@MYTEST2> select index_name, partition_name, status from user_ind_partitions;
INDEX_NAME                     PARTITION_ STATUS
------------------------------ ---------- --------
ORDERS_IDX_TS_LOCAL            PART_2010  USABLE
ORDERS_IDX_TS_LOCAL            PART_2011  USABLE

tony@MYTEST2> alter index orders_idx_id_global rebuild;
Index altered.


到此为止滑动窗口过程几乎不会带来任何停机时间,但是在我们重建全局索引时,需要相当长的时间才能完成。
可以在分区操作时使用UPDATE GLOBAL INEXES 子句来维护全局索引,对于需要提供数据连续访问的系统来说是很有用的。
通过牺牲分区操作的速度,可以换取100%的数据可用性(尽管分区操作的总体响应时间会更慢)。
如果数据仓库不允许有停机时间,而且必须支持数据的滑入滑出等数据仓库技术,这个特性就非常合适。

 

你可能感兴趣的:(oracle,编程,Date,table,less,数据仓库)