Partition Tables介绍及分区表转换

--====================

-- Partition Tables

--====================

分区表相关概念:

当表数据不断增加时,查询数据库速度就回变慢,应用程序性能就会下降,这个时候就该考虑对表进行分区管理。分区表在逻辑上任然是一张完整的表,只是在物理上可能存放在多个表空间或物理文件上。当查询数据是,不至于每次都扫描整张表,避免热块争用。这样比全表扫描能提供更好的数据处理能力和访问能力。

限制条件:

1. 簇表不适用与分区

2. 不能分割含有LONG,LOG RAW字段的表

3. 索引朱指标(IOT)表不能进行范围分区。

分区表优点:

1. 增强可用性:

2. 减少关闭时间

3.可维护性

4.均衡I/O

5.提高性能

6.对应用,用户透明

分区表类别:

1. 范围分区 range partition

2. 哈希分区 hash partition

3. 列表分区 list partition

4. 引用分区 reference partition

5. 复合分区 composite partition

6. 间隔分区 interval partition

7. 系统分区

相关视图:

select partitioning_type,table_name,interval from user_part_tables

select table_name,partition_name,tablespace_name,interval from user_tab_partitions;

select partitioned,owner,table_name from dba_tables;

显示子分区信息,dba_tab_subpartitions

显示分区列 dba_part_key_columns

显示子分区列 dba_subpart_key_columns

分区转换方法:

1. 导入导出 (imp/impdp)

2. 子查询(subquery )

3. 分区转发 partition exchange

4. 在线重定义 dbms_redefinition

1.1 Export/import method

采用逻辑导出导入很简单,首先在源库建立分区表,然后将数据导出,然后导入到新建的分区表即可,

1)  导出表:

expdp \'/ as sysdba\' directory=DATA_PUMP_DIR dumpfile=lhr_t.dmp  INCLUDE=TABLE:\"IN \(\'T\'\)\" SCHEMAS=LHR LOGFILE=expdp_T.log

2)  删除表:drop table numbers;

3)  重建分区表的定义:

    create table numbers (qty number(3), name varchar2(15))

    partition by range (qty)

    (partition p1 values less than (501),

     partition p2 values less than (maxvalue));

4)  利用ignore=y来导入分区表:

impdp \'/ as sysdba\' directory=DATA_PUMP_DIR dumpfile=lhr_t.dmp SCHEMAS=LHR table_exists_action=APPEND LOGFILE=impdp_T.log

5) 查询导入后的情况

SYS@dlhr> SELECT D.TABLE_OWNER,D.TABLE_NAME,D.PARTITION_NAME FROM DBA_TAB_PARTITIONS d WHERE d.table_name='T';

2.1 利用原表重建分区表(插入)

这种方法的特点是:

优点:方法简单易用,由于采用DDL语句,不会产生UNDO,且只产生少量REDO,效率相对较高,而且建表完成后数据已经在分布到各个分区中了。

不足:对于数据的一致性方面还需要额外的考虑。由于几乎没有办法通过手工锁定T表的方式保证一致性,在执行CREATE TABLE语句和RENAME T_NEW TO T语句直接的修改可能会丢失,如果要保证一致性,需要在执行完语句后对数据进行检查,而这个代价是比较大的。另外在执行两个RENAME语句之间执行的对T的访问会失败。

 适用于修改不频繁的表,在闲时进行操作,表的数据量不宜太大。

主要有2种方式,ctas和insert方式,下边分别介绍:

2.1.1  例一:CTAS+RENAME

利用CTAS语法在创建分区表的时候可以一起插入数据,也可以创建好表结构再insert 进去。 CTAS这种方法采用DDL语句,不产生UNDO,只产生少量REDO,建表完成后数据已经在分布到各个分区中。

模拟环境:

SQL> create table part1 (id number, time date);

表已创建。

SQL> insert into part1 select rownum,created from dba_objects;

已创建72807行。

SQL> commit;

提交完成。

SQL> select to_char(time,'yyyymmdd'),count(1) from part1 group by to_char(time,'yyyymmdd');

TO_CHAR(TIME,'YY COUNT(1)

---------------- ----------

20180323 39

20180324 39

20180326 39

20180321 4414

20180330 42

20180322 20

20180331 72

20180325 39

20180327 39

20100330 68018

20180329 46

已选择11行。

SQL> create table t_new (id,time) partition by range(time)(

2 partition t1 values less than(to_date('20180323','yyyymmdd')),

3 partition t2 values less than(to_date('20180324','yyyymmdd')),

4 partition t3 values less than(to_date('20180325','yyyymmdd')),

5 partition t4 values less than(to_date('20180326','yyyymmdd')),

6 partition t5 values less than(to_date('20180329','yyyymmdd')),

7 partition t6 values less than (maxvalue)) as select id,time from part1;

表已创建。

注意:如果表数据量分厂,如上方法是非常损耗性能的,当然我们可以使用添加‘并行DDL’,‘加nologging’和‘查询并行’方式来提高转换性能。

SQL> create table t_new (id,time) partition by range(time)(

2 partition t1 values less than(to_date('20180323','yyyymmdd')),

3 partition t2 values less than(to_date('20180324','yyyymmdd')),

4 partition t3 values less than(to_date('20180325','yyyymmdd')),

5 partition t4 values less than(to_date('20180326','yyyymmdd')),

6 partition t5 values less than(to_date('20180329','yyyymmdd')),

7 partition t6 values less than (maxvalue))

as select /*+parallel*/ id,time from part1;

2.1.2  例二: Insert with a subquery method

这种方法就是先建立表结构然后使用insert 来实现。

创建分区表:

LHR@dlhr> CREATE TABLE T_LHR_20160527_NEW (ID NUMBER, TIME DATE)

  2  PARTITION BY RANGE (TIME)

  3      (PARTITION T1 VALUES LESS THAN (TO_DATE('201311', 'YYYYMM')),

  4       PARTITION T2 VALUES LESS THAN (TO_DATE('201606', 'YYYYMM')),

  5       PARTITION T3 VALUES LESS THAN (MAXVALUE));

从源表查询插入到新表中:

LHR@dlhr> alter table T_LHR_20160527_NEW nologging;

Table altered.

LHR@dlhr> alter session enable parallel dml;

Session altered.

LHR@dlhr> insert /*+APPEND PARALLEL*/ into T_LHR_20160527_NEW (ID, TIME) select /*+PARALLEL(t3,4)*/  * from T_LHR_20160527;

注意:采用并行DML必须执行alter session enable parallel dml;

3.1  使用交换分区的方法(Partition exchange method)

这种方法的特点

 优点:只是对数据字典中分区和表的定义进行了修改,没有数据的修改或复制,效率最高。如果对数据在分区中的分布没有进一步要求的话,实现比较简单。在执行完RENAME操作后,可以检查T_OLD中是否存在数据,如果存在的话,直接将这些数据插入到T中,可以保证对T插入的操作不会丢失。

不足:仍然存在一致性问题,交换分区之后RENAME T_NEW TO T之前,查询、更新和删除会出现错误或访问不到数据。如果要求数据分布到多个分区中,则需要进行分区的SPLIT操作,会增加操作的复杂度,效率也会降低。

转换之后,原表上的索引,约束需要重新建立。

 适用于包含大数据量的表转到分区表中的一个分区的操作。应尽量在闲时进行操作。

3.1.1 模拟环境:

SQL> create table obj as select object_name,object_id,created from user_objects;

SQL> alter table obj add constraint obj_idx primary key(object_id);

已创建72807行。

创建分区:

create table obj_range(

object_name varchar2(128) ,

object_id number ,

created date,

constraint obj_indx primary key(object_id))

partition by range(created)(

partition r1 values less than(to_date('2018/06/01','yyyy/mm/dd')) tablespace p1,

partition r2 values less than(to_date('2018/07/01','yyyy/mm/dd')) tablespace p2,

partition r3 values less than(maxvalue));

表已创建。

注意:如果是单分区,需要r1分区包含转换的表obj的所有数据,不然会报错误:

第 1 行出现错误:

ORA-14099: 未对指定分区限定表中的所有行

转换数据:

SQL> alter table obj_range exchange partition r1 with table obj;

Table altered.

SQL> alter index SH.OBJ_INDX rebuild online;

Index altered.

SQL> insert into obj_range values('obj',100,to_date('2018/05/21','yyyy/mm/dd'));

1 row created.

查看分区信息:

SQL> select table_name,PARTITION_NAME,SUBPARTITION_COUNT,TABLESPACE_NAME from user_tab_partitions where table_name='OBJ_RANGE';

在数据转换的时候很难保证有新数据插入,所以在转换完成之后需要对老表数据查询看是否有新数据存在,有的话直接掺入到新分区表中。

如果要求数据分布到多个分区中,则需要进行分区的SPLIT操作。

原表跟分区表字段类型,长度,约束要保持一致。

转换的分区表名字改成跟老表一样。

3.1.2. 如果分区表存在多个分区,就需要对分区进行单独转换

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

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

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

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

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

     5. 用表B 和p2 分区交换。 把表B的数据存放到P2分区。

SQL> alter table part exchange partition SAL1 with table sh.sales1;

Table altered.

SQL> alter table part exchange partition SAL2 with table sh.sales2;

Table altered.

4.1 利用在线重定义功能(DBMS_REDEFINITION)

这种分区的特点

优点:保证数据的一致性,在大部分时间内,表T都可以正常进行DML操作。只在切换的瞬间锁表,具有很高的可用性。这种方法具有很强的灵活性,对各种不同的需要都能满足。而且,可以在切换前进行相应的授权并建立各种约束,可以做到切换完成后不再需要任何额外的管理操作。

 不足:实现上比上面两种略显复杂。

 适用于各种情况。

在线重定义的大致操作流程如下:

       (1)创建基础表A(数据表),如果存在,就不需要操作。

       (2)创建临时的分区表B结构。

       (3)开始重定义,将基表A的数据导入临时分区表B。

       (4)结束重定义,完成后在DB的 Name Directory里,已经将2个表进行了交换。即此时基表A成了分区表,我们创建的临时分区表B 成了普通表。 此时我们可以删除我们创建的临时表B。它已经是普通表。

4.1.2 在线重定义功能

这个功能只在9.2.0.4以后的版本才有,在线重定义表具有以下功能:

       (1)修改表的存储参数;

       (2)将表转移到其他表空间;

       (3)增加并行查询选项;

       (4)增加或删除分区;

       (5)重建表以减少碎片;

       (6)将堆表改为索引组织表或相反的操作;

       (7)增加或删除一个列。

4.1.3在线重定义表的步骤

在线重定义的原理:物化视图

在线重定义表的步骤:

1.选择一种重定义方法:

存在两种重定义方法,一种是基于主键、另一种是基于ROWID。ROWID的方式不能用于索引组织表(IOT),而且重定义后会存在隐藏列M_ROW$$。默认采用主键的方式。

2.调用DBMS_REDEFINITION.CAN_REDEF_TABLE()过程,如果表不满足重定义的条件,将会报错并给出原因。

3.在用一个方案中建立一个空的中间表,根据重定义后你期望得到的结构建立中间表。比如:采用分区表,增加了COLUMN等。

4.调用DBMS_REDEFINITION.START_REDEF_TABLE()过程,并提供下列参数:被重定义的表的名称、中间表的名称、列的映射规则、重定义方法。

如果映射方法没有提供,则认为所有包括在中间表中的列用于表的重定义。如果给出了映射方法,则只考虑映射方法中给出的列。如果没有给出重定义方法,则认为使用主键方式。

5.在中间表上建立触发器、索引和约束,并进行相应的授权。任何包含中间表的完整性约束应将状态置为disabled。

当重定义完成时,中间表上建立的触发器、索引、约束和授权将替换重定义表上的触发器、索引、约束和授权。中间表上disabled的约束将在重定义表上enable。

6.(可选)如果在执行DBMS_REDEFINITION.START_REDEF_TABLE()过程和执行DBMS_REDEFINITION.FINISH_REDEF_TABLE()过程直接在重定义表上执行了大量的DML操作,那么可以选择执行一次或多次的SYNC_INTERIM_TABLE()过程,以减少最后一步执行FINISH_REDEF_TABLE()过程时的锁定时间。

7.执行DBMS_REDEFINITION.FINISH_REDEF_TABLE()过程完成表的重定义。这个过程中,原始表会被独占模式锁定一小段时间,具体时间和表的数据量有关。

执行完FINISH_REDEF_TABLE()过程后,原始表重定义后具有了中间表的属性、索引、约束、授权和触发器。中间表上disabled的约束在原始表上处于enabled状态。

8.(可选)可以重命名索引、触发器和约束。对于采用了ROWID方式重定义的表,包括了一个隐含列M_ROW$$。推荐使用下列语句经隐含列置为UNUSED状态或删除。

ALTER TABLE TABLE_NAME SET UNUSED (M_ROW$$);

ALTER TABLE TABLE_NAME DROP UNUSED COLUMNS;

4.1.4 使用在线重定义的限制条件

使用在线重定义的一些限制条件:

(1) There must be enough space to hold two copies of the table.

(2) Primary key columns cannot be modified.

(3) Tables must have primary keys.

(4) Redefinition must be done within the same schema.

(5) New columns added cannot be made NOT NULL until after the redefinition operation.

(6) Tables cannot contain LONGs, BFILEs or User Defined Types.

(7) Clustered tables cannot be redefined.

(8) Tables in the SYS or SYSTEM schema cannot be redefined.

(9) Tables with materialized view logs or materialized views defined on them cannot be redefined.

(10) Horizontal sub setting of data cannot be performed during the redefinition.

?如果使用基于主键的方式,则原表后重定义后的表必须有相同的主键

?  如果使用基于ROWID的方式,则不能是索引组织表

?  如果原表上有物化视图或者物化视图日志,则不能在线重定义

?  物化视图容器表或者高级队列表不能在线重定义

?  索引组织表的溢出表不能在线重定义

?  拥有BFILE,LOGN列的表不能在线重定义

?  Cluster中的表不能在线重定义

?  sys和system下的表不能在线重定义

?  临时表不能在线重定义

?  不支持水平数据子集

?  在列映射时只能使用有确定结果的表达式,如子查询就不行

?  如果中间表有新增列,则不能有NOT NULL约束

?  原表和中间表之间不能有引用完整性

?  在线重定义无法采用nologging

4.1.5 模拟环境:

SQL> create table obj(id number, time date);

表已创建。

SQL> insert into obj select rownum,created from dba_objects;

已创建72820行。

SQL> commit;

提交完成。

SQL> create index date_idx on obj(time);

索引已创建。

SQL> exec dbms_stats.gather_table_stats('PLAT','OBJ',cascade=>true);

PL/SQL 过程已成功完成。

创建临时分区表:

create table obj_range(

object_name varchar2(128) ,

object_id number ,

created date,

constraint obj_indx primary key(object_id))

partition by range(created)(

partition r1 values less than(to_date('2018/06/01','yyyy/mm/dd')) tablespace p1,

partition r2 values less than(to_date('2018/07/01','yyyy/mm/dd')) tablespace p2,

partition r3 values less than(maxvalue));

表已创建。

表合法性检查:检查是否可以执行在线重定义,若返回错误的话说明不能执行,需要根据提示修改表

SQL> exec dbms_redefinition.can_redef_table('PLAT','OBJ',dbms_redefinition.cons_use_pk);

BEGIN dbms_redefinition.can_redef_table('PLAT','OBJ',dbms_redefinition.cons_us

e_pk); END;

*

第 1 行出现错误:

ORA-12089: 不能联机重新定义无主键的表 "PLAT"."OBJ"

ORA-06512: 在 "SYS.DBMS_REDEFINITION", line 139

ORA-06512: 在 "SYS.DBMS_REDEFINITION", line 1782

ORA-06512: 在 line 1

SQL> alter table plat.OBJ add constraint idx_pk primary key (id);

表已更改。

SQL> exec dbms_redefinition.can_redef_table('PLAT','OBJ',dbms_redefinition.cons_use_pk);

PL/SQL 过程已成功完成。

开始在线重定义:

SQL> exec dbms_redefinition.start_redef_table('PLAT','OBJ','OBJ_RANGE',dbms_redef

inition.cons_use_pk);

BEGIN dbms_redefinition.start_redef_table('PLAT','OBJ','OBJ_RANGE',dbms_redefinit

ion.cons_use_pk); END;

*

第 1 行出现错误:

ORA-42016: 中间表的形式与指定的列映射不匹配

ORA-06512: 在 "SYS.DBMS_REDEFINITION", line 52

ORA-06512: 在 "SYS.DBMS_REDEFINITION", line 1646

ORA-06512: 在 line 1

表列映射:

SQL> exec dbms_redefinition.start_redef_table('PLAT','OBJ','OBJ_RANGE','id id,time created_date ',dbms_redefinition.cons_use_pk);

PL/SQL 过程已成功完成。

SQL> select count(1) from OBJ;

COUNT(1)

----------

72821

SQL> select count(1) from OBJ_RANGE;

COUNT(1)

----------

72820

由上可见基表多一条数据,很有可能是在进行表在线重定义的时候,基表有数据插入,这个时候我们需要让基表数据跟中间表同步

SQL> exec dbms_redefinition.sync_interim_table('PLAT','OBJ','OBJ_RANGE');

PL/SQL 过程已成功完成。

猜你喜欢

转载自my.oschina.net/u/3862440/blog/2873353