局部分区索引
使用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%的数据可用性(尽管分区操作的总体响应时间会更慢)。
如果数据仓库不允许有停机时间,而且必须支持数据的滑入滑出等数据仓库技术,这个特性就非常合适。