Oracle之分区技术

 

一、分区理论知识

  Oracle提供了分区技术以支持VLDB(Very Large DataBase)。分区表通过对分区的判断,把分区列不同的记录,放到不同的分区中。分区完全对应用透明。

  Oracle的分区表可以包括多个分区,每个分区都是一个独立的段(SEGMENT),可以存放到不同的表空间中。查询时可以通过查询表来访问各个分区中的数据,也可以通过在查询时直接指定分区的方法来进行查询。

1.1、When to Partition a Table(什么时候需要分区表,官网的2个建议如下:):

  (1)、Tables greater than 2GB should always be considered for partitioning.

  (2)、Tables containing historical data,in which new data is added into the newest partition.A typical example is a historical table where only the current month's data is updatable and the other 11 months are read only.

1.2、在Oracle 10g中最多支持:1024k-1个分区:

  Tables can be partitioned into up to 1024k-1 separate partitions.

1.3、分区特点:

  (1)、可以单独对分区及分区索引进行操作。在分区对象中,可以只对单独分区进行数据加载、数据备份、数据恢复以及索引重建等操作,而不必对整个对象进行操作。

  (2)、提高查询效率。在某些时候分区让查询可以更快,因为Oracle有一个分区裁剪功能,只对需要处理的分区进行扫描,这样扫描的数据块会大大地减少,使查询效率提高。

  (3)、分区更利于数据维护。可以只对单独分区进行备份、恢复,这样就可以大大缩短数据备份、恢复的时间。

  (4)、分区有利于数据库数据的过期化处理。

1.4、分区的种类

  在Oracle 10g中,按照官方文档的描述,分区可以分为下面三种:

  (1)、范围分区(Range partiton)

  (2)、哈希分区(Hash partition)

  (3)、列表分区(List partition)

1.5、Range分区:

  Range分区是应用范围比较广的表分区方式,它是以列的值的范围来做为分区的划分条件,将记录存放在列值所在的range分区中。

  如按照时间划分,2010年1月的数据放到a分区,2月的数据放到b分区,在创建的时候,需要指定基于的列,以及分区的范围值。

  在按时间分区时,如果某些记录暂无法预测范围,可以创建maxvalue分区,所有不在指定范围内的记录都会被存储到maxvalue所在分区中。

  创建语句如:

1 SQL> create table jack_range (id number,time date) partition by range(time)
2   2  (
3   3     partition p1 values less than (to_date('2012-10-1','yyyy-mm-dd')),
4   4     partition p2 values less than (to_date('2012-11-1','yyyy-mm-dd')),
5   5     partition p3 values less than (to_date('2012-12-1','yyyy-mm-dd')),
6   6     partition p4 values less than (maxvalue)
7   7  );
8 
9 Table created.

1.6、Hash分区:

  对于那些无法有效划分范围的表,可以使用hash分区,这样对于提高性能还是有一定的帮助。hash分区会将表中的数据平均分配到你指定的几个分区中,列所在分区是依据分区列的hash值自动分配,因此你并不能控制也不知道哪条记录会被放到哪个分区中,hash分区也可以支持多个依赖列。

  创建语句:

 1 SQL> create table jack_hash
 2   2    (id number primary key,name varchar2(10),type varchar2(10))
 3   3     partition by hash(id)
 4   4  (
 5   5     partition hash_01 tablespace jack_hash01,     --在这里,我们指定了每个分区的表空间
 6   6     partition hash_02 tablespace jack_hash02,
 7   7     partition hash_03 tablespace jack_hash03
 8   8  );
 9 
10 Table created.

 1.7、List分区:
  List分区也需要指定列的值,其分区值必须明确指定,该分区列值只能有一个,不能像range或者hash分区那样同时指定多个列做为分区依赖列,但它的单个分区对应值可以是多个。

  在分区时必须确定分区列可能存在的值,一旦插入的列值不在分区范围内,则插入/更新就会失败,因此通常建议使用list分区时,要创建一个default分区存储那些不在指定范围内的记录,类似range分区中的maxvalue分区。

  在根据某字段,如城市代码分区时,可以指定default,把非分区规则的数据,全部放到这个default分区。

  创建语句:

 1 SQL> create table jack_list
 2   2    (id number not null,name varchar2(10),type varchar2(10))
 3   3    partition by list(type)
 4   4  (
 5   5    partition list_01 values ('oracle','mysql'),
 6   6    partition list_02 values ('MongoDB','SimpleDB'),
 7   7    partition list_other values (default)
 8   8  );
 9 
10 Table created.

二、普通表转分区表方法

  将普通表转成分区表有4中方法:

     1.Export/Import method;

     2.Insert with a subquery method;

     3.Partition exchange method;

     4.DBMS_REDEFINITION.

   在这里对于1和2的方法我们将不做讨论。

2.1、交换分区:Partition exchange method

     这种方法只是对数据字典中分区和表的定义进行了修改,没有数据的修改或复制,效率最高。适用于包含大数据量的表转到分区表中的一个分区的操作。尽量在闲时进行操作。

     交换分区的操作步骤如下:

          1.创建分区表,假设有2个分区,P1,P2。

          2.创建表A存放P1分区规则的数据。

          3.创建表B存放P2分区规则的数据。

          4.用表A和P1分区交换,把表A的数据放到P1分区。

          5.用表B和P2分区交卷,把表B的数据放到P2分区。

 

三、分区表的其他操作

  3.1、添加新的分区

   (1)、原分区里边界是maxvalue或者default。这种情况下,我们需要把边界分区drop掉,加上新分区后,在添加上新的分区。或者采用split,对边界分区进行拆分。

   (2)、没有边界分区的。这种情况下,直接添加分区就可以了。

  以Range分区添加新分区示例(此处使用的用户是sys):

   (1)、创建分区表和索引:

 1 SQL> create table range_jack
 2   2     (id int not null,name varchar2(10),type varchar2(10))
 3   3     partition by range(id)
 4   4  (
 5   5     partition range_10 values less than (10) tablespace range01,
 6   6     partition range_max values less than (maxvalue) tablespace range02
 7   7  );
 8 
 9 Table created.
10 
11 SQL> create index ind_range on range_jack(id)
12   2   local
13   3  (
14   4    partition ind_10 tablespace range01,
15   5    partition ind_max tablespace range02
16   6  );
17 
18 Index created.

    (2)、插入几条测试数据: 

 1 SQL> insert into range_jack values (1,'aa','aaa');
 2 
 3 1 row created.
 4 
 5 SQL> insert into range_jack values (20,'gg','ggg');
 6 
 7 1 row created.
 8 
 9 SQL> commit;
10 
11 Commit complete.
12 
13 SQL> select * from range_jack;    
14 
15     ID NAME       TYPE
16 ---------- ---------- ----------
17      1 aa          aaa
18     20 gg          ggg
19 
20 SQL> select * from range_jack partition(range_max);
21 
22     ID NAME       TYPE
23 ---------- ---------- ----------
24     20 gg          ggg

    (3)删除maxvalue分区:

1 SQL> alter table range_jack drop partition range_max;
2 
3 Table altered.
4 
5 SQL> select table_name,partition_name from user_tab_partitions where table_name='RANGE_JACK';
6 
7 TABLE_NAME               PARTITION_NAME
8 ------------------------------ ------------------------------
9 RANGE_JACK               RANGE_10

    (4)、添加新分区:

 1 SQL> alter table range_jack add partition range_20 values less than (20) tablespace range02;
 2 
 3 Table altered.
 4 
 5 SQL> select table_name,partition_name from user_tab_partitions where table_name='RANGE_JACK';
 6 
 7 TABLE_NAME               PARTITION_NAME
 8 ------------------------------ ------------------------------
 9 RANGE_JACK               RANGE_10
10 RANGE_JACK               RANGE_20

    (5)、添加maxvalue分区:

 1 SQL> alter table range_jack add partition range_max values less than (maxvalue) tablespace range03;
 2 
 3 Table altered.
 4 
 5 SQL> select table_name,partition_name from user_tab_partitions where table_name='RANGE_JACK';
 6 
 7 TABLE_NAME               PARTITION_NAME
 8 ------------------------------ ------------------------------
 9 RANGE_JACK               RANGE_10
10 RANGE_JACK               RANGE_20
11 RANGE_JACK               RANGE_MAX

    (6)、对于局部索引,Oracle会自动增加一个局部分区索引。验证一下:

 1 SQL> show user;
 2 USER is "SYS"
 3 SQL> select owner,index_name,table_name,partitioning_type from dba_part_indexes where index_name='IND_RANGE';
 4 
 5 OWNER       INDEX_NAME    TABLE_NAME   PARTITION
 6 ---------- ------------ ------------ ---------
 7 SYS       IND_RANGE    RANGE_JACK   RANGE
 8 
 9 SQL> select index_owner,index_name,partition_name from dba_ind_partitions where index_name='IND_RANGE';
10 
11 INDEX_OWNER           INDEX_NAME   PARTITION_NAME
12 ------------------- ------------ ------------------------------
13 SYS                   IND_RANGE    RANGE_MAX           --分区索引自动创建
14 SYS                   IND_RANGE    RANGE_20
15 SYS                   IND_RANGE    IND_10

   3.2、split分区拆分

       在3.1中,我们说明了可以使用split的方式来添加分区。这里我们用split方法继续上面的实验。

 1 SQL> alter table range_jack split partition range_max at(25) into (partition range_25 tablespace yft,partition range_max tablespace yft);
 2 
 3 Table altered.
 4 --------注意这里红色的地方,如果是Range类型的,使用at;List使用Values.
5 SQL> select table_name,partition_name from user_tab_partitions where table_name='RANGE_JACK'; 6 7 TABLE_NAME PARTITION_NAME 8 ------------------------------ ------------------------------ 9 RANGE_JACK RANGE_10 10 RANGE_JACK RANGE_20 11 RANGE_JACK RANGE_25 12 RANGE_JACK RANGE_MAX 13 14 SQL> select index_owner,index_name,partition_name from dba_ind_partitions where index_name='IND_RANGE'; 15 16 INDEX_OWNE INDEX_NAME PARTITION_NAME 17 ---------- ---------- ------------------------------ 18 JACK IND_RANGE IND_10 19 JACK IND_RANGE RANGE_20 20 JACK IND_RANGE RANGE_25 21 JACK IND_RANGE RANGE_MAX
  注意:分区表会自动维护局部分区索引。全局索引会失效,需要进行rebuild。

3.3、合并分区Merge
    相邻的分区可以merge为一个分区,新分区的下边界为原来边界值较低的分区,上边界为原来边界值较高分区,原先的局部索引相应也会合并,全局索引会失效,需要rebuild。

 1 SQL> alter table range_jack merge partitions range_25,range_max into partition range_max;
 2 
 3 Table altered.
 4 
 5 SQL> select table_name,partition_name from user_tab_partitions where table_name='RANGE_JACK';
 6 
 7 TABLE_NAME               PARTITION_NAME
 8 ------------------------------ ------------------------------
 9 RANGE_JACK               RANGE_10
10 RANGE_JACK               RANGE_20
11 RANGE_JACK               RANGE_MAX
12 
13 SQL> select index_owner,index_name,partition_name from dba_ind_partitions where index_name='IND_RANGE';
14 
15 INDEX_OWNE INDEX_NAME PARTITION_NAME
16 ---------- ---------- ------------------------------
17 JACK       IND_RANGE  IND_10
18 JACK       IND_RANGE  RANGE_20
19 JACK       IND_RANGE  RANGE_MAX

3.4、移动分区

 1 SQL> alter table range_jack move partition range_max tablespace users;
 2 
 3 Table altered.
 4 
 5 SQL> alter table range_jack move partition range_max tablespace yft;
 6 
 7 Table altered.
 8
---注意:分区移动会自动维护局部分区索引,oracle不会自动维护全局索引,所以需要我们重新rebuild分区索引,具有需要rebuild哪些索引,
可以通过dba_part_indexes,dba_ind_partitions去判断。 9 SQL> select index_name,status from user_indexes where table_name='RANGE_JACK'; 10 11 INDEX_NAME STATUS 12 ---------- -------- 13 IND_RANGE N/A

3.5、Truncate分区

 1 SQL> select * from range_jack partition(range_10);
 2 
 3     ID NAME       TYPE
 4 ---------- ---------- ----------
 5      1 aa          aaa
 6 
 7 SQL> alter table range_jack truncate partition(range_10);
 8 
 9 Table truncated.
10 
11 SQL> select * from range_jack partition(range_10);
12 
13 no rows selected
  说明:Truncate相对delete操作很快,数据仓库中的大量数据的批量数据加载可能会有用到;截断分区同样会自动维护局部分区索引,同时会使全局索引unusable,需要重建。

3.6、Drop分区

 1 SQL> alter table range_jack drop partition range_10;
 2 
 3 Table altered.
 4 
 5 SQL> select table_name,partition_name from user_tab_partitions where table_name='RANGE_JACK';
 6 
 7 TABLE_NAME               PARTITION_NAME
 8 ------------------------------ ------------------------------
 9 RANGE_JACK               RANGE_20
10 RANGE_JACK               RANGE_MAX
   注意:同样会自动维护局部分区索引,同时会使全局索引unusable,需要重建。

四、分区表的索引

     分区索引分为本地索引(local index)和全局索引(global index),其中局部索引比全局索引容易管理,至于速度的比较请看: 《关于分区索引与全局索引性能比较的示例》

     与索引有关的表:

     dba_part_indexes:分区索引的概要统计信息,可以得知每个表上有哪些分区索引,分区索引的类型(local/global);

     dba_ind_partitions:每个分区索引的分区级统计信息;

     dba_indexes/dba_part_indexes:可以得到每个表上有哪些非分区索引。

     注意:Local索引肯定是分区索引,Global索引可以选择是否分区,如果分区,只能是有前缀的分区索引。

 分区索引分2类:有前缀(prefix)的分区索引和无前缀(nonprefix)的分区索引:
(1)、有前缀的分区索引指包含了分区键,并且将其作为引导列的索引。
1
SQL> create index global_ind on range_jack(id) global ----引导列 2 2 partition by range(id) ---分区键
3 3 ( 4 4 partition g1 values less than (20), 5 5 partition gm values less than (maxvalue) 6 6 ); 7 -----说明:这里的ID就是分区键,并且分区键id也是索引的引导列 8 Index created. 9
(2)、无前缀的分区索引的列不是以分区键开头,或者不包含分区键列。
10 SQL> create index local_ind on range_jack(name) 11 2 local 12 3 ( 13 4 partition l_20 tablespace yft, 14 5 partition l_max tablespace yft 15 6 ); 16 17 Index created. 18 ----说明:这个分区按照name来的。但是索引的引导列是id,所以它就是非前缀分区索引。

全局分区索引不支持非前缀的分区索引,如果创建,报错如下: 19 SQL> create index global_ind on range_jack(id) global 20 2 partition by range (name) 21 3 ( 22 4 partition p1 values less than ('aa'), 23 5 partition p2 values less than (maxvalue) 24 6 ); 25 partition by range (name) 26 * 27 ERROR at line 2: 28 ORA-14038: GLOBAL partitioned index must be prefixed

 4.1、Local index(本地索引)

        对于Local索引,当表的分区发生变化时,索引的维护由Oracle自动进行。

注意事项:

(1)、局部索引一定是分区索引,分区键等同于表的分区键。

(2)、前缀和非前缀索引都可以支持索引分区消除,前提是查询的条件中包含索引分区键。

(3)、局部索引只支持分区内的唯一性,无法支持表上的唯一性,因此如果要用局部索引去给表做唯一性约束,则约束中必须要包括分区键列。

(4)、局部分区索引是对单个分区的,每个分区索引只指向一个表分区;全局索引则不然,一个分区索引能指向n个表分区,同时,一个表分区,也可能指向n个索引分区,对分区表中的某个分区做truncate或者move,shrink等,可能会影响到n个全局索引分区,正因为这点,局部分区索引具有更高的可用性。

(5)、位图索引必须是局部分区索引。

(6)、局部索引多应用于数据仓库环境中。

(7)、B树索引和位图索引都可以分区。

示例:

 
1
SQL> create index locals_index on range_jack(id) 2 2 local 3 3 ( 4 4 partition l_20 tablespace yft, 5 5 partition l_max tablespace yft 6 6 ); 7 8 Index created. 9 10 SQL> create index local_ind on range_jack(name) local; 11 12 Index created. 13 ----验证2个索引的类型-------------
14
SQL> select index_name,table_name,partitioning_type,locality,alignment from user_part_indexes where table_name='RANGE_JACK'; 15 16 INDEX_NAME TABLE_NAME PARTITION LOCALI ALIGNMENT 17 --------------- ---------- --------- ------ ------------ 18 LOCALS_INDEX RANGE_JACK RANGE LOCAL PREFIXED 19 LOCAL_IND RANGE_JACK RANGE LOCAL NON_PREFIXED

4.2、Global索引

     对于global索引,可以选择是否分区,而且索引的分区可以不与表分区相对应。全局分区索引只能是B树索引,在10gR2之前。

     另外Oracle不会自动的维护全局分区索引,当我们在对表的分区做修改之后,如果对分区进行维护操作时不加上update global indexes的话,通常会导致全局索引的INVALDED,必须在执行完操作后REBUILD。

注意事项:

(1)、全局索引可以分区,也可以是不分区索引,全局索引必须是有前缀索引,即全局索引的索引必须是以索引分区键作为其前几列。

(2)、全局索引可以依附于分区表;也可以依附于非分区表。

(3)、全局分区索引的索引条目可能指向若干个分区,因此,对于全局分区索引,即使只截断一个分区中的数据,都需要rebuild若干个分区甚至是整个索引。

(4)、全部索引多应用于oltp系统中。

(5)、全局分区索引只按范围或者散列分区,hash分区是10g以后才支持。

(6)、oracle9i以后对分区表做move或者truncate的时可以用update global indexes语句来同步更新全局分区索引,用消耗一定资源来换取高度的可用行。

(7)、表用a列做分区,索引用b做局部分区索引,若where条件中用b来查询,那么oracle会扫描所有的表和索引的分区,成本会比分区更高,此时可以考虑b做全局分区索引。

----示例1:全局索引,全局索引对所有分区类型都支持:
1
SQL> create index global_jack_ind on range_jack(id) global; 2 3 Index created.
 ----示例2:全局分区索引,只支持Range分区和Hash分区:
----创建2个测试分区表---------------------------
1
SQL> create table jack_range(id number,time date) partition by range(time) 2 2 ( 3 3 partition p1 values less than (to_date('2013-01-1','yyyy-mm-dd')), 4 4 partition p2 values less than (to_date('2013-02-1','yyyy-mm-dd')), 5 5 partition p3 values less than (to_date('2013-03-1','yyyy-mm-dd')), 6 6 partition p4 values less than (maxvalue) 7 7 ); 8 9 Table created. 10 11 SQL> create table jack_hash(id number primary key,item_id number(8) not null) 12 2 partition by hash(id) 13 3 ( 14 4 partition part_01, 15 5 partition part_02, 16 6 partition part_03 17 7 ); 18 19 Table created. 20 ----创建全局分区索引-----------------
21
SQL> create index jack_global_ind on jack_range(id) global 22 2 partition by range(id) 23 3 ( 24 4 partition g1 values less than(200), 25 5 partition g2 values less than(maxvalue) 26 6 ); 27 28 Index created. 29 ----------------这个是有前缀的分区索引。
30
SQL> create index jack_global_time on jack_range(id) global 31 2 partition by range(time) 32 3 ( 33 4 partition g1 values less than(to_date('2013-02-1','YYYY-MM-DD')), 34 5 partition g2 values less than(maxvalue) 35 6 ); 36 partition by range(time) 37 * 38 ERROR at line 2: 39 ORA-14038: GLOBAL partitioned index must be prefixed 40 41 42 SQL> create index jack_global_time on jack_range(time) global 43 2 partition by range(time) 44 3 ( 45 4 partition g1 values less than(to_date('2013-02-1','YYYY-MM-DD')), 46 5 partition g2 values less than(maxvalue) 47 6 ); 48 49 Index created. 50 -----------------有前缀的分区索引。
51
SQL> select index_name,table_name,partitioning_type,locality,alignment from user_part_indexes where table_name='JACK_RANGE'; 52 53 INDEX_NAME TABLE_NAME PARTITION LOCALI ALIGNMENT 54 ------------------------------ ---------- --------- ------ ------------ 55 JACK_GLOBAL_IND JACK_RANGE RANGE GLOBAL PREFIXED 56 JACK_GLOBAL_TIME JACK_RANGE RANGE GLOBAL PREFIXED 57 58 SQL> create index hash_global on jack_range(id,time) global 59 2 partition by hash(id) 60 3 ( 61 4 partition p1, 62 5 partition p2, 63 6 partition p3, 64 7 partition p4 65 8 ); 66 67 Index created.

4.3、索引重建

   (1)、分区索引

   对于分区索引,不能整体进行重建,只能对单个分区进行重建。语法如下:

   Alter index index_name rebuild partition index_partition_name [online nologging];

说明:

       online:表示重建的时候不会锁表。

       nologging:表示建立索引的时候不生成日志,加快速度。

如果要重建分区索引,只能drop表原索引,在重新创建:

      SQL>create index loc_xxxx_col on xxx(col) local tablespace yft;

      这个操作要求较大的临时表空间和排序区。

示例:

 1 SQL> select index_name,partition_name from user_ind_partitions where index_name='LOCAL_IND';
 2 
 3 INDEX_NAME               PARTITION_NAME
 4 --------------------- ------------------------------
 5 LOCAL_IND               RANGE_20
 6 LOCAL_IND               RANGE_MAX
 7 
 8 SQL> alter index local_ind rebuild partition range_20 online nologging;
 9 
10 Index altered.
11 
12 SQL> alter index local_ind rebuild partition range_max online nologging;
13 
14 Index altered.

     (2)、全局索引

     Oracle会自动维护分区索引,对于全局索引,如果在对分区表操作时,没有指定update index,则会导致全局索引失效,需要重建。

 

 1 SQL> select owner,index_name,table_name,status from dba_indexes where index_name='GLOBAL_IND';
 2 
 3 OWNER       INDEX_NAME              TABLE_NAME STATUS
 4 ---------- --------------------    ---------- --------
 5 JACK       GLOBAL_IND              RANGE_JACK VALID
 6 
 7 SQL> alter table range_jack drop partition range_20;
 8 
 9 Table altered.
10 
11 SQL> select owner,index_name,table_name,status from dba_indexes where index_name='GLOBAL_IND';
12 
13 OWNER       INDEX_NAME              TABLE_NAME STATUS
14 ---------- ----------------------- ---------- --------
15 JACK       GLOBAL_IND              RANGE_JACK VALID

 

重建全局索引命令如下:

       Alter index idx_name rebuild [online nologging]

五、参考资料: 

http://blog.csdn.net/tianlesoftware/article/details/4717318

http://blog.csdn.net/kaertiger/article/details/6927568

http://docs.oracle.com/cd/E11882_01/server.112/e25789/schemaob.htm#CFAGCHCD

 

posted @ 2012-12-17 16:14  I’m Me!  阅读(1970)  评论(0编辑  收藏  举报