分区技术纷繁复杂,从11g以后有更多的分区方式,但是在考虑使用分区之前要想好是否需要历史数据清理。
比如罗敏的《品悟性能优化》书中P138那个案例。如果使用命名方式分区,那么以后清理历史数据无法根据分区特性进行清理,原因在于可能同一个名字的分区中包含好几年的数据。
Oracle 分区表简单而言就是将一张大表按一定规则分为物理上的很多小表,而逻辑上仍然维持为一张大表。对应用是透明的。
分区索引分为以下几种情况
・ 表和索引都不分区
・ 表分区,索引没分区
・ 表没分区,索引分区
・ 表和索引都分区
先做几个实验,来感受感受分区表和分区索引。
本地分区索引指索引的分区方法与对应表的分区方法一样,
在分区删除(DROP)操作之后,普通索引和全局分区索引都会失效(INVALID),必须重建。
本地分区索引又分为本地前缀分区索引,和本地非前缀分区索引
本地前缀分区索引,就是以分区键创建的索引,比如test_range_part 表以object_id 为分区键,那么在object_id上创建索引就是本地前缀分区索引。
类似:
create index idx_part_object_id on test_range_part(object_id) local; create index idx_part_object_id on test_range_part(object_id,object_name) local;
本地非前缀分区索引,就是以非分区键创建的索引,比如test_range_part表以object_id为分区键,那么在object_name上创建索引就是本地非前缀分区索引。
类似:
create index idx_part_object_name on test_range_part(object_name) local;
本地非前缀分区索引的好处在于,在分区删除(DROP)操作之后,该本地非前缀分区索引依然有效。
本地非前缀分区索引的缺点这里说一下,比如我想查询object_name='EMP'的行,由于object_name不是分区键,那么有一种可能,object_name在所有分区中都有值,比如EMP可能会在obj_10000和obj_20000... 等分区出现。那么通过object_name='EMP'条件回表访问的时候,可能会访问所有的本地分区索引。每次都访问不同的segment。如果我的索引不进行分区,那么可以直接通过大的索引直接访问。
存在即合理,为什么非前缀分区索引仍然会用?因为在做数据清理的时候,可能会对分区进行drop,如果是本地非前缀分区索引,分区drop之后其他分区索引不会失效。如果这时候使用了非分区索引,那就死了。整个索引都失效,创建索引的时候还锁表!!!
所以在创建索引之前要权衡好。
创建分区表,下面创建了一张范围分区表。
create table test_range_part (object_id number, owner varchar2(30), object_name varchar2(128)) partition by range (object_id)( partition obj_10000 values less than(10000), partition obj_20000 values less than(20000), partition obj_30000 values less than(30000), partition obj_40000 values less than(40000), partition obj_50000 values less than(50000), partition obj_60000 values less than(60000), partition obj_70000 values less than(70000), partition obj_80000 values less than(80000), partition obj_max values less than(maxvalue) ); insert into test_range_part select object_id,owner,object_name from test; commit;
以object_name为索引列
create index idx_part_object_name on test_range_part(object_name) local;
以owner为索引列
create index idx_part_owner on test_range_part(owner) local;
从下面查询可以看出,本地费前缀分区索引的分区方式仍然是按照分区表的分区条件进行分区的。
从下面查询视图中,num_rows列可以看出每个分区有多少行数据
SELECT INDEX_OWNER, INDEX_NAME, PARTITION_NAME, PARTITION_POSITION, TABLESPACE_NAME, DISTINCT_KEYS, NUM_ROWS, LAST_ANALYZED FROM DBA_IND_PARTITIONS WHERE INDEX_OWNER = 'SCOTT';
运行下面查询看看每个分区有多少数据
SELECT COUNT(*) FROM TEST_RANGE_PART PARTITION(OBJ_10000) UNION ALL SELECT COUNT(*) FROM TEST_RANGE_PART PARTITION(OBJ_20000) UNION ALL SELECT COUNT(*) FROM TEST_RANGE_PART PARTITION(OBJ_30000) UNION ALL SELECT COUNT(*) FROM TEST_RANGE_PART PARTITION(OBJ_40000) UNION ALL SELECT COUNT(*) FROM TEST_RANGE_PART PARTITION(OBJ_50000) UNION ALL SELECT COUNT(*) FROM TEST_RANGE_PART PARTITION(OBJ_60000) UNION ALL SELECT COUNT(*) FROM TEST_RANGE_PART PARTITION(OBJ_70000) UNION ALL SELECT COUNT(*) FROM TEST_RANGE_PART PARTITION(OBJ_80000) UNION ALL SELECT COUNT(*) FROM TEST_RANGE_PART PARTITION(OBJ_MAX);
下面实验对range分区表清理数据,并查看相关索引状态
alter table test_range_part drop partition(obj_10000);
从下面查询得到结论,如果对分区进行drop之后,其他分区的索引都可以使用,状态都是usable状态。
SELECT STATUS, INDEX_NAME, PARTITION_NAME, PARTITION_POSITION, TABLESPACE_NAME, DISTINCT_KEYS, NUM_ROWS, LAST_ANALYZED FROM DBA_IND_PARTITIONS WHERE INDEX_OWNER = 'SCOTT';
SELECT INDEX_NAME, TABLE_NAME, PARTITIONING_TYPE, PARTITION_COUNT, LOCALITY FROM DBA_PART_INDEXES WHERE OWNER = 'SCOTT' AND INDEX_NAME IN ('IDX_PART_OWNER', 'IDX_PART_OBJECT_NAME');
刚才本地分区表中,不管以什么列创建索引都是以分区表分区键分区。这里的全局分区索引不再使用分区键作为分区条件,而是自定义一个分区条件。创建索引。
10g之前只有全局范围分区表,10g之后又多出了全局哈希分区表
下面实验用来验证当分区被drop后,全局分区索引会失效
用下面方法创建分区表,同样是范围分区表,比刚才的分区表多了很多个列。
drop table test_range_part1 purge; create table test_range_part1 partition by range (object_id)( partition obj_10000 values less than(10000), partition obj_20000 values less than(20000), partition obj_30000 values less than(30000), partition obj_40000 values less than(40000), partition obj_50000 values less than(50000), partition obj_60000 values less than(60000), partition obj_70000 values less than(70000), partition obj_80000 values less than(80000), partition obj_max values less than(maxvalue) ) as select * from test;
这里有个小插曲,关于创建本地分区索引,对索引分区命名的方法。
用下面方法创建分区索引,并且自定义分区名称。如果分区名称不定义,那么默认跟表分区名相同。
drop index idx_test_objid; CREATE INDEX idx_test_objid ON test_range_part1(object_id) LOCAL (PARTITION part1, PARTITION part2, PARTITION part3, PARTITION part4, PARTITION part5, PARTITION part6, PARTITION part7, PARTITION part8, PARTITION part9);
同样如果本地分区索引创建指定了分区名,而且定义的索引分区比表分区还要多,那么将要报错,ORA-14024
如下图:
根据创建时间(created)列创建全局分区索引
drop index idx_part_created; CREATE INDEX idx_part_created ON test_range_part1(created) GLOBAL PARTITION BY RANGE(created) (PARTITION part1 VALUES LESS THAN(to_date('2013-06-01','yyyy-mm-dd')) , PARTITION part2 VALUES LESS THAN(to_date('2014-01-01','yyyy-mm-dd')) , PARTITION part3 VALUES LESS THAN(to_date('2014-06-01','yyyy-mm-dd')) , PARTITION part4 VALUES LESS THAN(to_date('2015-01-01','yyyy-mm-dd')) , PARTITION part5 VALUES LESS THAN(to_date('2015-06-01','yyyy-mm-dd')) , PARTITION part6 VALUES LESS THAN(to_date('2016-01-01','yyyy-mm-dd')) , PARTITION part7 VALUES LESS THAN(to_date('2016-06-01','yyyy-mm-dd')) , PARTITION partmaxvalue VALUES LESS THAN(MAXVALUE));
查询每个索引分区的行数。
SELECT STATUS, INDEX_NAME, PARTITION_NAME, PARTITION_POSITION, TABLESPACE_NAME, DISTINCT_KEYS, NUM_ROWS, LAST_ANALYZED FROM DBA_IND_PARTITIONS WHERE INDEX_OWNER = 'SCOTT' AND INDEX_NAME = 'IDX_PART_CREATED';
如何分别全局分区索引和本地分区索引。
SELECT INDEX_NAME, TABLE_NAME, PARTITIONING_TYPE, PARTITION_COUNT, LOCALITY FROM DBA_PART_INDEXES WHERE OWNER = 'SCOTT';
删除分区obj_10000
alter table test_range_part1 drop partition (obj_10000);
查看全局分区索引状态
发现IDX_PART_CREATED索引的状态都变成了UNUSABLE,已经不可用了。为什么会这样呢?
当创建全局分区索引的时候索引列使用的created会遍布到分区表的各个分区中。
如果我删掉了其中一个分区,那么全局分区索引的每个分区中都会缺少分区表中第一个分区的数据。所以导致全局分区索引失效。
如果不好理解我举个例子。可能created有100条2015-10-10。而分区表是按照object_id进行分区的。这100条2015-10-10可能分布在obj_10000,obj_20000,obj_30000,MAXVALUE中,当我做了drop partition 操作后,导致索引中的数据丢失。
所以做完drop partition 操作后要做一个rebuild操作。
使用这条查询语句拼出创建索引的语句:
SELECT 'alter index ' || INDEX_OWNER || '.' || INDEX_NAME || ' rebuild partition ' || PARTITION_NAME ||';' FROM DBA_IND_PARTITIONS WHERE STATUS = 'UNUSABLE'; 'ALTERINDEX'||INDEX_OWNER||'.'||INDEX_NAME||'REBUILDPARTITION'||PARTITION_NAME -------------------------------------------------------------------------------------------------------------------------- alter index SCOTT.IDX_PART_CREATED rebuild partition PARTMAXVALUE; alter index SCOTT.IDX_PART_CREATED rebuild partition PART7; alter index SCOTT.IDX_PART_CREATED rebuild partition PART6; alter index SCOTT.IDX_PART_CREATED rebuild partition PART5; alter index SCOTT.IDX_PART_CREATED rebuild partition PART4; alter index SCOTT.IDX_PART_CREATED rebuild partition PART3; alter index SCOTT.IDX_PART_CREATED rebuild partition PART2; alter index SCOTT.IDX_PART_CREATED rebuild partition PART1; 8 rows selected.
使用上面语句创建好索引后,查看视图,得到下面的结果。
从status列能看出全局分区索引都已经重建好,并且可以使用了。
SELECT INDEX_NAME, INDEX_TYPE, TABLESPACE_NAME, STATUS, PARTITIONED, GLOBAL_STATS, SEGMENT_CREATED FROM DBA_INDEXES WHERE OWNER = 'SCOTT';
GLOBAL_STATS列的意思。如果是YES,那么使用analyze命令会收统计信息,如果是NO,那么使用analyze命令不会收集统计信息,只有分区索引和子分区索引才是NO状态。
SEGMENT_CREATED的意思为是否分配了SEGMENT。
在实际环境中更多使用如下方法对索引进行维护
在分区表上创建普通索引 SQL> create index idx_norm_name on test_range_part(object_name); Index created. SQL> select table_name,partition_name from dba_tab_partitions where table_owner='SCOTT'; TABLE_NAME PARTITION_NAME ------------------------------ ------------------------------ TEST_RANGE_PART1 OBJ_MAX TEST_RANGE_PART1 OBJ_80000 TEST_RANGE_PART1 OBJ_70000 TEST_RANGE_PART1 OBJ_60000 TEST_RANGE_PART1 OBJ_50000 TEST_RANGE_PART1 OBJ_40000 TEST_RANGE_PART1 OBJ_30000 TEST_RANGE_PART1 OBJ_20000 TEST_RANGE_PART OBJ_MAX TEST_RANGE_PART OBJ_80000 TEST_RANGE_PART OBJ_70000 TABLE_NAME PARTITION_NAME ------------------------------ ------------------------------ TEST_RANGE_PART OBJ_60000 TEST_RANGE_PART OBJ_50000 TEST_RANGE_PART OBJ_40000 14 rows selected. 删掉分区表TEST_RANGE_PART上的一个分区 SQL> alter table test_range_part drop partition obj_40000; Table altered. 查看索引状态,其中unusable状态的就是刚才创建的idx_norm_name 如果恰好你的实验也到这一步,那么可以通过dba_indexes 这个视图得到查询结果 这里我就不查询了。 SQL> select distinct status from dba_indexes; STATUS -------- UNUSABLE VALID N/A 重建索引的方式已经在前面介绍过了,语法不同的是多了一个rebuild online SQL> alter index idx_norm_name rebuild online; Index altered. 再次查看索引状态,idx_norm_name重建好之后,dba_indexes中unusable状态的索引就消失了 SQL> select distinct status from dba_indexes; STATUS -------- VALID N/A 再次删除一个分区,但是删除分区后面跟了一句update global indexes 表示删除分区的时候同时维护索引。 SQL> alter table test_range_part drop partition obj_50000 update global indexes; Table altered. 发现没有索引是unusable状态了。 前面这种update global indexes 同样适用于全局分区索引。 SQL> select distinct status from dba_indexes; STATUS -------- VALID N/A