再上一篇:12.9小结
上一篇:13.1分区概述
主页
下一篇:13.3索引分区
再下一篇:13.4再论分区和性能
文章列表

13.2表分区机制

Oracle 9i 10g编程艺术:深入数据库体系结构

目前Oracle中有4种对表分区的方法: 区间分区:可以指定应当存储在一起的数据区间。例如,时间戳在 Jan-2005内的所有记录
都存储在分区1 中,时间戳在Feb-2005内的所有记录都存储在分区2中,依此类推。这可能是 Oracle中最常用的分区机制。
散列分区:我们在这一章一个例子中就已经看到了散列分区。这是指在一个列(或多个列) 上应用一个散列函数,行会按这个散列值放在某个分区中。
列表分区:指定一个离散值集,来确定应当存储在一起的数据。例如,可以指定 STATUS列 值在(’A’,’M’,’Z’)中的行放在分区1中,STATUS值在(‘D’,’P’,’Q’)中的行放在 分区2 中,依此类推。
组合分区:这是区间分区和散列分区的一种组合,或者是区间分区与列表分区的组合。通 过组合分区,你可以先对某些数据应用区间分区,再在区间中根据散列或列表来选择最后的分 区。
在 下面几节中,我们将介绍各类分区的好处,以及它们之间有什么差别。我们还会说明什么时候应 当对不同类型的应用采用何种机制。这一节并不打算对分区语法和所 有可用选项提供一个全面的介绍。相 反,我们要靠例子说话,这些例子很简单,也很直观,专门设计为帮助你了解分区,使你对分区如何工作 以及如何设计不同类型 的分区有一个简要认识。
注意 要想全面地了解分区语法的所有细节,建议你阅读 Oracle SQL Reference Guide 或 Oracle Administrator’s Guide。另外,Oracle Data Warehousing Guide 也是一个不错的信息源,其 中对分区选项做了很好的说明,对于每个计划实现分区的人来说,这是必读的文档。

13.2.1区间分区


我们要介绍的第一种类型是区间分区表(range partitioned table)。下面的 CREATE TABLE语句创 建了一个使用RANGE_KEY_COLUMN列的区间分区表。RANGE_KEY_COLUMN值严格小于01-JAN-2005的所有数 据要放在分区PART_1中,RANGE_KEY_COLUMN值严格小于01-JAN-2006的所有数据则放在分区PART_2 中。 不满足这两个条件的所有数据(例如,RANGE_KEY_COLUMN值为01-JAN-2007的行)将不能插入,因为它们 无法映射到任何分区:
ops$tkyte@ORA10GR1> CREATE TABLE range_example

2 ( range_key_column date ,
3 data varchar2(20)
4 )
5 PARTITION BY RANGE (range_key_column)
6 ( PARTITION part_1 VALUES LESS THAN
7 (to_date('01/01/2005','dd/mm/yyyy')),
8 PARTITION part_2 VALUES LESS THAN
9 (to_date('01/01/2006','dd/mm/yyyy'))
10 )
11 /
Table created.
注意 我们在CREATE TABLE语句中使用了日期格式DD/MM/YYYY,使之做到“国际化”。如果使用格式
DD-MON-YYYY,倘若在你的系统上一月份的缩写不是Jan,这个CREATE TABLE 语句就会失败,而 得到一个ORA-01843:not a valid month错误。NLS_LANGUAGE设置会对此产生影响。不过,我在 正文和插入语句中使用了3字符的月份缩写,以避免月和日产生歧义,否则有时很难区分哪个分 量是日,哪个分量是月。
图13-1显示了Oracle会检查RANGE_KEY_COLUMN的值,并根据这个值,将数据插入到两个分区之一。

图13-1 区间分区插入示例
为了展示分区区间是严格小于某个值而不是小于或等于某个值,这里插入的行是特别选择的。我们首 先插入值15-DEC-2004,它肯定要放在分区PART_1 中。我们还插入了日期/时间为01-JAN-2005之前一秒
(31-dec-2004 23:59:59)的行,这一行也会放到分区PART_1中,因为它小于01-JAN-2005。不过,插 入的下一行日期/时间不是严格小于分区区间边界。最后一行显然应该放在分区PART_2 中,因为它小于 PART_2的分区区间边界。

可以从各个分区分别执行SELECT语句,来确认确实如此:
ops$tkyte@ORA10G> select to_char(range_key_column,'dd-mon-yyyy hh24:mi:ss')
2 from range_example partition (part_1); TO_CHAR(RANGE_KEY_CO
--------------------
15-dec-2004 00:00:00
31-dec-2004 23:59:59
ops$tkyte@ORA10G> select to_char(range_key_column,'dd-mon-yyyy hh24:mi:ss')
2 from range_example partition (part_2);

TO_CHAR(RANGE_KEY_CO
--------------------
01-jan-2005 00:00:00
15-dec-2005 00:00:00
你可能想知道,如果插入的日期超出上界会怎么样呢?答案是Oracle会产生一个错误:
ops$tkyte@ORA10GR1> insert into range_example
2 ( range_key_column, data )
3 values
4 ( to_date( '15/12/2007 00:00:00',
5 'dd/mm/yyyy hh24:mi:ss' ),
6 'application data...' );
insert into range_example
* ERROR at line 1:
ORA-14400: inserted partition key does not map to any partition

假设你想像刚才一样,将2005年和2006年的日期分别聚集到各自的分区,但是另外你还希望将所有

其他日期都归入第三个分区。利用区间分区,这可以使用MAXVALUE子句做到这一点,如下所示:
ops$tkyte@ORA10GR1> CREATE TABLE range_example
2 ( range_key_column date ,
3 data varchar2(20)
4 )
5 PARTITION BY RANGE (range_key_column)
6 ( PARTITION part_1 VALUES LESS THAN
7 (to_date('01/01/2005','dd/mm/yyyy')),
8 PARTITION part_2 VALUES LESS THAN

9 (to_date('01/01/2006','dd/mm/yyyy'))
10 PARTITION part_3 VALUES LESS THAN
11 (MAXVALUE)
12 )
13 /
Table created.
现在,向这个表插入一个行时,这一行肯定会放入三个分区中的某一个分区中,而不会再拒绝任何行 ,
因为分区PART_3可以接受不能放在PART_1或PART_2中的任何RANG_KEY_COLUMN值(即使 RANGE_KEY_COLUMN 值为null,也会插入到这个新分区中)。

13.2.2散列分区

对一个表执行散列分区(hash partitioning)时,Oracle会对分区键应用一个散列函数,以此确定 数据应当放在N 个分区中的哪一个分区中。Oracle建议N 是2 的一个幂(2、4、8、16等),从而得到最佳 的总体分布,稍后会看到这确实是一个很好的建议。

1. 散列分区如何工作

散列分区设计为能使数据很好地分布在多个不同设备(磁盘)上,或者只是将数据聚集到更可管理的 块(chunk)上,为表选择的散列键应当是惟一的一个列或一组列,或者至少有足够多的相异值,以便行能 在多个分区上很好地(均匀地)分布。如果你选择一个只有4 个相异值的列,并使用两个分区,那么最后 可能把所有行都散列到同一个分区上,这就有悖于分区的最初目标!

在这里,我们将创建一个有两个分区的散列表。在此使用名为HASH_KEY_COLUMN的列作为分区键。 Oracle会取这个列中的值,并计算它的散列值,从而确定这一行将存储在哪个分区中:
ops$tkyte@ORA10G> CREATE TABLE hash_example
2 ( hash_key_column date,
3 data varchar2(20)
4 )
5 PARTITION BY HASH (hash_key_column)
6 ( partition part_1 tablespace p1,
7 partition part_2 tablespace p2
8 )

9 /
Table created.
图13-2显示了Oracle会检查HASH_KEY_COLUMN中的值,计算散列,确定给定行会出现在两个分区中
的哪一个分区中:

图13-2 散列分区插入示例 前面已经提到过,如果使用散列分区,你将无从控制一行最终会放在哪个分区中。Oracle会应用散
列函数,并根据散列的结果来确定行会放在哪里。如果你由于某种原因希望将某个特定行放在分区PART_1 中, 就不应该使用散列分区,实际上,此时也不能使用散列分区。行会按散列函数的“指示”放在某个分 区中,也就是说,散列函数说这一行该放在哪个分区,它就会放 在哪个分区中。如果改变散列分区的个数 , 数据会在所有分区中重新分布(向一个散列分区表增加或删除一个分区时,将导致所有数据都重写,因为 现在每一行可能 属于一个不同的分区)。
如果你有一个大表(如“减少管理负担”一节中所示的表),而且你想对它“分而治之”,此时散列分 区最有用。你不用管理一个大表,而只是管理8或16个 较小的“表”。从某种程度上讲,散列分区对于提 高可用性也很有用,这在“提高可用性”一节中已经介绍过;临时丢掉一个散列分区,就能访问所有余下 的分区。 也许有些用户会受到影响,但是很有可能很多用户根本不受影响,但是很有可能很多用户根本不 受影响。另外,恢复的单位现在也更小了。你不用恢复一个完整的大 表;而只需恢复表中的一小部分。最 后一点,散列分区还有利于存在高度更新竞争的环境,这在“改善语句性能”一节讨论OLTP系统时已经提 到过。我们可以不使一个段“很热”,而是可以将一个段散列分区为16个“部分”,这样一来,现在每一部 分都可以接收修改。

2. 散列分区数使用2 的幂


我在前面提到过,分区数应该是2的幂,这很容易观察。为了便于说明,我们建立了一个存储过程, 它会自动创建一个有N个分区的散列分区表(N是一个参数)。这个过程会构成一个动态查询,按分区获取 其中的行数,再按分区显示行数,并给出行数的一个简单直方图。最后,它会打开这个查询,以便我们看 到结果。这个过程首先创建散列表。我们将使用一个名为T的表:
ops$tkyte@ORA10G> create or replace
2 procedure hash_proc
3 ( p_nhash in number,
4 p_cursor out sys_refcursor )
5 authid current_user
6 as
7 l_text long;
8 l_template long :=
9 'select $POS$ oc, ''p$POS$'' pname, count(*) cnt ' ||
10 'from t partition ( $PNAME$ ) union all ';
11 begin
12 begin
13 execute immediate 'drop table t';
14 exception when others
15 then null;
16 end;
17
18 execute immediate '
19 CREATE TABLE t ( id )
20 partition by hash(id)
21 partitions ' || p_nhash || '

22 as
23 select rownum
24 from all_objects';
接下来,动态构造一个查询,按分区获取行数。这里使用了前面定义的“模板”查询。对于每个分区 ,
我们将使用分区扩展的表名来收集分区中的行数,并把所有行数合在一起:
25
26 for x in ( select partition_name pname,
27 PARTITION_POSITION pos
28 from user_tab_partitions
29 where table_name = 'T'
30 order by partition_position )
31 loop
32 l_text := l_text ||
33 replace(
34 replace(l_template,
35 '$POS$', x.pos),
36 '$PNAME$', x.pname );
37 end loop;

现在,取这个查询,选出分区位置(PNAME)和该分区中的行数(CNT)。通过使用 RPAD,可以构造一

个相当基本但很有效的直方图:
38
39 open p_cursor for
40 'select pname, cnt,
41 substr
( rpad(''*'',30*round( cnt/max(cnt)over(),2),''*''),1,30) hg
42 from (' || substr( l_text, 1, length(l_text)-11 ) || ')

43 order by oc';
44
45 end;
46 /
Procedure created.
如果针对输入值4运行这个过程,这表示有4个散列分区,就会看到类似如下的输出:
ops$tkyte@ORA10G> variable x refcursor
ops$tkyte@ORA10G> set autoprint on ops$tkyte@ORA10G> exec hash_proc( 4, :x ); PL/SQL procedure successfully completed.
PN CNT HG
-- ---------- ------------------------------ p1 12141 *****************************
p2 12178 *****************************
p3 12417 ******************************
p4 12105 *****************************

这个简单的直方图展示了数据很均匀地分布在这4个分区中。每个分区中的行数都很接近。不过,如

果将4 改成5,要求有5个散列分区,就会看到以下输出:
ops$tkyte@ORA10G> exec hash_proc( 5, :x );
PL/SQL procedure successfully completed. PN CNT HG
-- ---------- ------------------------------ p1 6102 **************
p2 12180 *****************************
p3 12419 ******************************

p4 12106 *****************************
p5 6040 **************
这个直方图指出,第一个和最后一个分区中的行数只是另外三个分区中行数的一半。数据根本没有得

到均匀的分布。我们会看到,如果有6 个和7个散列分区,这种趋势还会继续:
ops$tkyte@ORA10G> exec hash_proc( 6, :x );
PL/SQL procedure successfully completed.
PN CNT HG
-- ---------- ------------------------------ p1 6104 **************
p2 6175 ***************
p3 12420 ****************************** p4 12106 ***************************** p5 6040 **************
p6 6009 **************
6 rows selected.
ops$tkyte@ORA10G> exec hash_proc( 7, :x ); PL/SQL procedure successfully completed.
PN CNT HG
-- ---------- ------------------------------ p1 6105 ***************
p2 6176 ***************
p3 6161 ***************

p4 12106 ******************************
p5 6041 *************** p6 6010 *************** p7 6263 ***************
7 rows selected.
散列分区数再回到2的幂值(8)时,又能到达我们的目标,实现均匀分布:
ops$tkyte@ORA10G> exec hash_proc( 8, :x );
PL/SQL procedure successfully completed.
PN CNT HG
-- ---------- ------------------------------ p1 6106 *****************************
p2 6178 ***************************** p3 6163 ***************************** p4 6019 **************************** p5 6042 **************************** p6 6010 ****************************
p7 6264 ******************************
p8 6089 *****************************
8 rows selected.

再继续这个实验,分区最多达到16个,你会看到如果分区数为9~15,也存在同样的问题,中间的
分区存放的数据多,而两头的分区中数据少,数据的分布是斜的;而达到16个分区时,你会再次看到数据 分布是直的。再达到32个分区和64个分区时也是如此。这个例子只是要指出:散列分区数要使用 2的幂, 这一点非常重要。

13.2.3列表分区

列表分区(list partitioning)是Oracle9i Release 1的一个新特性。它提供了这样一种功能,可
以根据离散的值列表来指定一行位于哪个分区。如果能根据某个代码来进行分区(如州代码或区代码),这 通常很有用。例如,你可能想把Maine州(ME)、New Hampshire州(NH)、Vermont州(VT)和Massachusetts 州(MA)中所有人的记录都归至一个分区中,因为这些州相互之间挨得很近,而且你的应用按地理位置来 查询数据。类似地,你可能希望将Connecticut州(CT)、Rhode Island州(RI)和New York州(NY)的 数据分组在一起。
对此不能使用区间分区,因为第一个分区的区间是ME到VT,第二个区间是CT到RI。这两个区间有 重叠。而且也不能使用散列分区,因为这样你就无法控制给定行要放到哪个分区中;而要由Oracle提供的 内置散列函数来控制。
利用列表分区,我们可以很容易地完成这个定制分区机制:
ops$tkyte@ORA10G> create table list_example
2 ( state_cd varchar2(2),
3 data varchar2(20)
4 )
5 partition by list(state_cd)
6 ( partition part_1 values ( 'ME', 'NH', 'VT', 'MA' ),
7 partition part_2 values ( 'CT', 'RI', 'NY' )
8 )
9 /
Table created.

图13-3显示了Oracle会检查STATE_CD列,并根据其值将行放在正确的分区中。

就像区间分区一样,如果我们想插入列表分区中未指定的一个值,Oracle会向客户应用返回一个合 适的错误。换句话说,没有DEFAULT分区的列表分区表会隐含地施加一个约束(非常像表上的一个检查约 束):
ops$tkyte@ORA10G> insert into list_example values ( 'VA', 'data' );
insert into list_example values ( 'VA', 'data' )
* ERROR at line 1:
ORA-14400: inserted partition key does not map to any partition

图13-3 列表分区插入示例
如果想像前面一样把这个7个州分别聚集到各自的分区中,另外把其余的所有州代码放在第三个分区 中(或者,实际上对于所插入的任何其他行,如果STATE_CD列值不是以上7个州代码之一,就要放在第三
个分区中),就可以使用 VALUE(S 语句中使用这个子句):
DEFAULT)子句。在此,我们将修改表,增加这个分区(也可以在CREATE TABLE
ops$tkyte@ORA10G> alter table list_example
2 add partition
3 part_3 values ( DEFAULT ); Table altered.
ops$tkyte@ORA10G> insert into list_example values ( 'VA', 'data' );
1 row created.

值列表中未显式列出的所有值都会放到这个(DEFAULT)分区中。关于DEFAULT的使用,有一点要注

意:一旦列表分区表有一个DEFAULT分区,就不能再向这个表中增加更多的分区了:
ops$tkyte@ORA10G> alter table list_example
2 add partition

3 part_4 values( 'CA', 'NM' );
alter table list_example
* ERROR at line 1:
ORA-14323: cannot add partition when DEFAULT partition exists
此时必须删除DEFAULT分区,如何增加PART_4,再加回DEFAULT分区。原因在于,原来DEFAULT分
区可以有列表分区键值为CA或NM的行,但增加PART_4之后,这些行将不再属于DEFAULT分区。

13.2.4组合分区

最后我们会看到组合分区(composite partitioning)的一些例子,组合分区是区间分区和散列分区 的组合,或者是区间分区与列表分区的组合。
在组合分区中,顶层分区机制总是区间分区。第二级分区机制可能是列表分区或散列分区(在 Oracle9i Release 1 及以前的版本中,只支持散列子分区,而没有列表分区)。有意思的是,使用组合分 区时,并没有分区段,而只有子分区段。分区本身并没有段(这就类似于分区表没有段)。数据物理的存储 在子分区段上,分区成为一个逻辑容器,或者是一个指向实际子分区的容器。

在下面的例子中,我们将查看一个区间-散列组合分区。在此对区间分区使用的列集不同于散列分区 使用的列集。并不是非得如此,这两层分区也可以使用同样的列集:
ops$tkyte@ORA10G> CREATE TABLE composite_example
2 ( range_key_column date,
3 hash_key_column int,
4 data varchar2(20)
5 )
6 PARTITION BY RANGE (range_key_column)
7 subpartition by hash(hash_key_column) subpartitions 2
8 (
9 PARTITION part_1
10 VALUES LESS THAN(to_date('01/01/2005','dd/mm/yyyy'))
11 (subpartition part_1_sub_1,

12 subpartition part_1_sub_2
13 ),
14 PARTITION part_2
15 VALUES LESS THAN(to_date('01/01/2006','dd/mm/yyyy'))
16 (subpartition part_2_sub_1,
17 subpartition part_2_sub_2
18 )
19 )
20 /
Table created.
在区间-散列组合分区中,Oracle首先会应用区间分区规则,得出数据属于哪个区间。然后再应用散
列函数,来确定数据最后要放在哪个物理分区中。这个过程如图13-4所示。

图13-4 区间-散列组合分区示例

因 此,利用组合分区,你就能把数据先按区间分解,如果认为某个给定的区间还太大,或者认为有 必要做进一步的分区消除,可以再利用散列或列表将其再做分解。有 意思的是,每个区间分区不需要有相 同数目的子分区;例如,假设你在对一个日期列完成区间分区,以支持数据净化(快速而且容易地删除所 有就数据)。在2004年,CODE_KEY_COLUMN值为“奇数”的数据量与CODE_KEY_COLUMN值为“偶数”的数 据量是相等的。但是到了2005年,你发现与奇数吗相关的记录数是偶数吗相关的记录数的两倍,所以你希 望对应奇数码有更多的子分区。只需定义更多的子分区,就能相当容易地做到这一点:
ops$tkyte@ORA10G> CREATE TABLE composite_range_list_example
2 ( range_key_column date,
3 code_key_column int,
4 data varchar2(20)
5 )
6 PARTITION BY RANGE (range_key_column)
7 subpartition by list(code_key_column)
8 (
9 PARTITION part_1
10 VALUES LESS THAN(to_date('01/01/2005','dd/mm/yyyy'))
11 (subpartition part_1_sub_1 values( 1, 3, 5, 7 ),
12 subpartition part_1_sub_2 values( 2, 4, 6, 8 )
13 ),
14 PARTITION part_2
15 VALUES LESS THAN(to_date('01/01/2006','dd/mm/yyyy'))
16 (subpartition part_2_sub_1 values ( 1, 3 ),
17 subpartition part_2_sub_2 values ( 5, 7 ),
18 subpartition part_2_sub_3 values ( 2, 4, 6, 8 )
19 )
20 )

21 /
Table created.
在此,最后总共有5个分区:分区PART_1有两个子分区,分区PART_2有3个子分区。

13.2.5行移动

你可能想知道,在前面所述的各种分区机制中,如果用于确定分区的列有修改会发生什么。需要考虑 两种情况:
修改不会导致使用一个不同的分区;行仍属于原来的分区。这在所有情况下都得到支持。 修改会导致行跨分区移动。只有当表启用了行移动时才支持这种情况;否则,会产生一个
错误。

这些行为很容易观察。在前面的例子中,我们向RANGE_EXAMPLE表的PART_1插入了两行:
ops$tkyte@ORA10G> insert into range_example
2 ( range_key_column, data )
3 values
4 ( to_date( '15-dec-2004 00:00:00',
5 'dd-mon-yyyy hh24:mi:ss' ),
6 'application data...' );
1 row created.
ops$tkyte@ORA10G> insert into range_example
2 ( range_key_column, data )
3 values
4 ( to_date( '01-jan-2005 00:00:00',
5 'dd-mon-yyyy hh24:mi:ss' )-1/24/60/60,
6 'application data...' );
1 row created.
ops$tkyte@ORA10G> select * from range_example partition(part_1);
RANGE_KEY DATA
--------- --------------------
15-DEC-04 application data...
31-DEC-04 application data...

取其中一行,并更新其RANGE_KEY_COLUMN值,不过更新后它还能放在PART_1中:
ops$tkyte@ORA10G> update range_example
2 set range_key_column = trunc(range_key_column)
3 where range_key_column =
4 to_date( '31-dec-2004 23:59:59',
5 'dd-mon-yyyy hh24:mi:ss' );
1 row updated.

不出所料,这会成功:行仍在分区 PART_1中。接下来,再把RANGE_KEY_COLUMN更新为另一个值,但
这次更新后的值将导致它属于分区PART_2:
ops$tkyte@ORA10G> update range_example
2 set range_key_column = to_date('02-jan-2005','dd-mon-yyyy')
3 where range_key_column = to_date('31-dec-2004','dd-mon-yyyy');
update range_example
*
ERROR at line 1:
ORA-14402: updating partition key column would cause a partition change

这会立即产生一个错误,因为我们没有显式地启用行移动。在Oracle8i及以后的版本中,可以在这
个表上启用行移动(row movement),以允许从一个分区移动到另一个分区。
注意 Oracle 8.0中没有行移动功能;在这个版本中,你必须先删除行,再重新将其插入。 不过,要注意这样做有一个小小的副作用;行的ROWID会由于更新而改变:

ops$tkyte@ORA10G> select rowid
2 from range_example
3 where range_key_column = to_date('31-dec-2004','dd-mon-yyyy'); ROWID
------------------ AAARmfAAKAAAI+aAAB
ops$tkyte@ORA10G> alter table range_example
2 enable row movement; Table altered.
ops$tkyte@ORA10G> update range_example
2 set range_key_column = to_date('02-jan-2005','dd-mon-yyyy')
3 where range_key_column = to_date('31-dec-2004','dd-mon-yyyy');
1 row updated.
ops$tkyte@ORA10G> select rowid
2 from range_example
3 where range_key_column = to_date('02-jan-2005','dd-mon-yyyy'); ROWID
------------------
AAARmgAAKAAAI+iAAC
既然知道执行这个更新时行的ROWID会改变,所以要启用行移动,这样才允许更新分区键。
注意 在其他一些情况下,ROWID也有可能因为更新而改变。更新 IOT 的主键可能导致 ROWID改变, 该行的通用ROWID(UROWID)也会改变。Oracle 10g的FLASHBACK TABLE 命令可能改变行的ROWID,
此外Oracle 10g的ALTER TABLE SHRINK命令也可能使行的ROWID改变。
要知道,执行行移动时,实际上在内部就好像先删除了这一行,然后再将其重新插入。这会更新这个 表上的索引,删除旧的索引条目,再插入一个新条目。此时会完成DELETE再加一个INSERT的相应物理工 作。不过,尽管在此执行了行的物理删除和插入,在Oracle看来却还是一个更新,因此,不会导致INSERT 和DELETE触发器触发,只有UPDATE触发器会触发。另外,由于外键约束可能不允许 DELETE的子表也不会 触发DELETE触发器。不过,还是要对将完成的额外工作有所准备;行移动的开销比正常的UPDATE昂贵得 多。因此,如果构建的系统会频繁修改分区键,而且这种修改会导致分区移动,这实在是一个糟糕的设计 决策。

13.2.6表分区机制小结

一般来讲,如果将数据按某个(某些)值逻辑聚集,区间分区就很有用。基于时间的数据就是这方面 经典的例子,如按“销售定额”、“财政年度”或“月份”分区。在许多情况下,区间分区都能利用分区消 除,这包括使用完全相等性和区间(小于、大于、介于…之间等)。
如 果不能按自然的区间进行分区,散列分区就很合适。例如,如果必须加载一个表,其中装满与人 口普查相关的数据,可能无法找到一个合适的属性来按这个属性完成 区间分区。不过,你可能还是想得到 分区提供的管理、性能和可用性提升等诸多好处。在此,只需选择惟一的一个列或几乎惟一的一个列集, 对其计算散列。这样一 来,无论有多少个分区,都能得到均匀的数据分布。使用完全相等性或 IN(value,value,…)时,散列分区对象可以利用分区消除,但是使用数据区间时,散列分区则无法利用 分区消除。
如 果数据中有一列有一组离散值,而且根据应用使用这一列的方式来看,按这一列进行分区很有意 义(例如,这样一来,查询中可以轻松地利用分区消除),这种数据 就很适合采用列表分区。列表分区的 经典例子包括按州或区域代码分区,实际上,一般来讲许多“代码”性属性都很适合应用列表分区。
如果某些数据逻辑上可以进行区间分区,但是得到的区间分区还是太小,不能有效地管理,就可以使 用组合分区。可以先应用区间分区,再进一步划分各个区间,按一个散列函数或使用列表来分区。这样就 能将I/O请求分布到任何给定大分区中的多个磁盘上。另外,现在可以得到3个层次的分区消除。如果在 区间分区键上查询,Oracle就能消除任何不满足条件的区间分区。如果向查询增加散列或列表键,Oracle 可以消除该区间中其他的散列或列表分区。如果只是在散列或列表键上查询(而不使用区间分区键),Oracle 就只会查询各个区间分区中的这些散列或列表子分区。
我们建议,如果可以按某个属性自然地对数据完成区间分区,就应该使用区间分区,而不是散列分区 或列表分区。散列和列表分区能提供分区的许多突出优点,但是在分区消除方面都不如区间分区有用。如 果所得到的区间分区太大,不能很好地管理;或者如果你想使用所有PDML功能或对一个区间分区使用并行 索引扫描,则建议在区间分区中再使用散列或列表分区。