面对越来越多的7*24系统,运维人员进行工作可用的时间窗口变的越来越小。就在有限的时间窗口中,硬件检修、网络改造配置占据了很多时间。对数据库对象进行日常维护,越来越成为我们需要关注的问题。
进行数据重排、表分区、字段类型修改、字段增改这样的操作,在开发和测试环境上是比较容易进行的。即使数据表很大,操作耗时可能会很高,我们也能够通过一些非技术的手段赢取操作时间窗。但是对于投产系统而言,操作过程中的长时间锁定可能是业务不能接受的。这个时候,就可以考虑Oracle的一些Online操作技术。
在笔者之前的一些文章中,介绍过一些online处理方法,如删除海量数据表,暂时隐去unused特定数据列,11g中默认值列优化等等。本篇我们介绍Oracle的Online Redefinition(在线重定义)特性。
1、 基础知识
Oracle Online Redefinition可以保证在数据表进行DDL类型操作,如插入、删除数据列,分区处理的时候,还能够支持DML操作,特别是insert/update/delete操作。
对一般的DDL过程而言,Oracle都会给数据对象一个独占表锁。也就是说,在进行DDL操作的过程中,我们是不能对数据表进行DML(增加、修改和删除操作)。只有等待DDL结束,才能够继续操作。
也就是说,如果一个DDL持续时间很长,比如数据表海量大小,那么在这个长时间中,系统数据表其实是不能对外提供服务的。
Oracle在线重定义提供了解决问题的途径。我们如果需要对一个数据表进行重定义,需要定义一个中间目标表Interim。在Interim表中定义好目标结构,比如期望的存储、分区、字段关系。在线重定义使用的主要是dbms_redefinition包的对应方法。
在线重定义最大的特点是,当进行online操作的时候,我们还可以对数据表进行DML操作。结束定义过程时,期间进行的操作都是可以反馈到目标数据表中的。
目前,Oracle在线重定义支持下列种类的重定义动作:
ü 插入、删除数据表列和对一个存在的数据表列改名;
ü 修改字段类型;
ü 消除数据表段中的碎片块;
ü 索引、约束等对象的重定义;
ü 分区表转变;
下面,我们通过一个简单的例子,去看看如何使用dbms_redefinition包进行重定义操作。
2、简单的重定义例子
我们在Oracle 11gR2中进行测试实验。
SQL> select * from v$version where rownum<3;
BANNER
--------------------------------------------------------------------------------
Oracle Database 11g Enterprise Edition Release 11.2.0.3.0 - Production
PL/SQL Release 11.2.0.3.0 – Production
首先我们创建目标数据表。
SQL> create table t as select object_id, object_name, owner from dba_objects;
Table created
Executed in 0.328 seconds
SQL> select count(*) from t;
COUNT(*)
----------
75192
Executed in 0.047 seconds
SQL> alter table t add constraint pk_t_id primary key (object_id);
Table altered
进行在线重定义的第一步,就是判断目标数据表是否可以进行重定义。此时,可以使用dbms_redefinition包的can_redef_table方法进行判断。
SQL> exec dbms_redefinition.can_redef_table( 'SCOTT','T',dbms_redefinition.cons_use_pk);
PL/SQL procedure successfully completed
Executed in 0.016 seconds
注意该方法的第三个参数,使用主键还是rowid方法。本质上,Online Redefinition是使用物化视图Materialized View技术。过程定义记录就是主键和rowid两种策略。通常而言,我们还是推荐数据表有一个明确主键,也就是使用cons_use_pk。如果希望使用rowid,就使用dbms_redefinition.cons_use_rowid。
通过了检查之后,就可以进行下一步,定义目标数据表格式。无论是何种变化,我们需要创建一个中间表interim,将我们“期望”的数据表定义实现在里面。其中包括表类型、列定义、分区定义和索引等。但是注意,约束(主外键)可以不定义在其中。
SQL> create table t_interim as select * from dba_objects where 1=0;
Table created
Executed in 0.032 seconds
显然,我们是希望给数据表T增加一些列。
此时,我们就可以开始重定义过程。使用dbms_redefinition的start_redef_table方法。
SQL> exec dbms_redefinition.start_redef_table('SCOTT','T','T_INTERIM',col_mapping => 'object_id object_id, object_name object_name, owner owner',options_flag => dbms_redefinition.cons_use_pk);
PL/SQL procedure successfully completed
Executed in 0.625 seconds
此处注意参数col_mapping,这里以配对键值的方式定义了源数据表和中间表在列关系上的对应关系。每个列关系按照<源列名 目标列名>的格式进行书写。
对约束的处理,如果有需要转换的约束对象,我们可以使用copy_table_dependents方法将source数据表的约束拷贝到目标对象中。
SQL> declare
2 error_count number;
3 begin
4 error_count := 0;
5 dbms_redefinition.copy_table_dependents(uname => 'SCOTT',orig_table => 'T',
6 int_table => 'T_INTERIM',
7 copy_indexes => dbms_redefinition.cons_orig_params,
8 num_errors => error_count);
9 dbms_output.put_line(to_char(error_count));
10 end;
11 /
0
PL/SQL procedure successfully completed
Executed in 4.265 seconds
这个方法的参数很多,定义如下:
-- NAME: copy_table_dependents
--
-- INPUTS: uname - schema name
-- orig_table - name of table to be re-organized
-- int_table - name of interim table
-- copy_indexes - integer value indicating whether to
-- copy indexes
-- 0 - don't copy
-- 1 - copy using storage params/tablespace
-- of original index
-- copy_triggers - TRUE implies copy triggers, FALSE otherwise
-- copy_constraints - TRUE implies copy constraints, FALSE
-- otherwise
-- copy_privileges - TRUE implies copy privileges, FALSE
-- otherwise
-- ignore errors - TRUE implies continue after errors, FALSE
-- otherwise
-- num_errors - number of errors that occurred while
-- cloning ddl
-- copy_statistics - TRUE implies copy table statistics, FALSE
-- otherwise.
-- If copy_indexes is 1, copy index
-- related statistics, 0 otherwise.
-- copy_mvlog - TRUE implies copy table's MV log, FALSE
-- otherwise.
PROCEDURE copy_table_dependents(uname IN VARCHAR2,
orig_table IN VARCHAR2,
int_table IN VARCHAR2,
copy_indexes IN PLS_INTEGER := 1,
copy_triggers IN BOOLEAN := TRUE,
copy_constraints IN BOOLEAN := TRUE,
copy_privileges IN BOOLEAN := TRUE,
ignore_errors IN BOOLEAN := FALSE,
num_errors OUT PLS_INTEGER,
copy_statistics IN BOOLEAN := FALSE,
copy_mvlog IN BOOLEAN := FALSE);
利用各种copy_xxx参数,我们可以精细的定义哪些约束依赖关系会被拷贝到目标表中。
当结束之后,我们需要使用finish_redef_table方法结束定义过程。
SQL> exec dbms_redefinition.finish_redef_table('SCOTT','T','T_INTERIM');
PL/SQL procedure successfully completed
Executed in 1.406 seconds
最后,我们查看效果。
SQL> desc t;
Name Type Nullable Default Comments
-------------- ------------- -------- ------- --------
OWNER VARCHAR2(30) Y
OBJECT_NAME VARCHAR2(128) Y
SUBOBJECT_NAME VARCHAR2(30) Y
OBJECT_ID NUMBER Y
DATA_OBJECT_ID NUMBER Y
OBJECT_TYPE VARCHAR2(19) Y
CREATED DATE Y
LAST_DDL_TIME DATE Y
TIMESTAMP VARCHAR2(19) Y
STATUS VARCHAR2(7) Y
TEMPORARY VARCHAR2(1) Y
GENERATED VARCHAR2(1) Y
SECONDARY VARCHAR2(1) Y
NAMESPACE NUMBER Y
EDITION_NAME VARCHAR2(30) Y
SQL> desc t_interim;
Name Type Nullable Default Comments
----------- ------------- -------- ------- --------
OBJECT_ID NUMBER
OBJECT_NAME VARCHAR2(128) Y
OWNER VARCHAR2(30) Y
SQL> select count(*) from t;
COUNT(*)
----------
75192
数据表T的字段被添加上,而中间表的结构被设置为原来的样子。约束内容,也就是原来的主键也被保留下来。
--在dbms_metadata.getddl输出片段
"EDITION_NAME" VARCHAR2(30),
CONSTRAINT "PK_T_ID" PRIMARY KEY ("OBJECT_ID")
至此,我们完成了一个最简单的重定义过程。大致分为五个步骤:
ü 判断数据表是否可以支持重定义,定义中间表Interim结构;
ü 使用dbms_redefinition的start_redef_table方法开始重定义过程;
ü 拷贝约束、重定义register约束信息内容;
ü 同步online过程中的DML操作(Optional,在之后例子演示);
ü 结束过程finish_redef_table操作;
对在线重定义,其中的细节过程,例如实现方式、中间DML操作同步、锁机制和各种方法的功能原理,我们在下面继续进行讨论。