【11g】dbms_redefinition 演示在线重定义

Oracle 支持在线重定义表,也就是说我们可以在修改表结构(DDL)的同时进行相关的DQL、DML操作,使得前端的DML根本感觉不到表结构实际上已经发生了变化,对于用户而言是完全透明的。当然在线重定义期间,前端性能会稍微有所下降。Oracle提供的重定义包dbms_redefinition即是用与完成此操作。其实质是Oracle使用了智能物化视图及物化视图日志的方式。在对象结构重组期间,表现为一个本地对象的复制,重组期间发生的任何变化都会被刷新到最新。

1、在线重定义表的主要功能:
     修改表或簇的存储参数
     将表移动到相同或不同schema下不同的tablespace(如果不要求表始终可用的话,也可以直接使用alter table move 实现)
     为表添加,修改或删除列
     为表添加或删除分区,改变分区结构
     改变物化视图日志或者Streams Advanced Queuing queue 表结构
     增加并行查询支持
     重建表以减少碎片
     将堆表变为索引组织表或相反

2、图示在线重定义
     下面的图示便于理解是如何进行在线重定义,其本质是基于基表的一个快照
        

3、在线重定义的步骤  
     a、选择在线重定义的方式,基于键(主键或唯一键)还是rowid(无主键或唯一键的情形)
     b、校验表能否被在线重定义,使用过程CAN_REDEF_TABLE 
     c、创建用于在线重定义的临时表(该临时表使用新的表结构,即添加删除列,列长度变化,存储属性变化等)
     d、如果重定义的为分区表且使用rowid方式,应该为临时表开启行移动(ALTER TABLE ... ENABLE ROW MOVEMENT;)
     e、对于大型表的在线重定义可以通过启用并行以提高性能(此步骤可选)
          ALTER SESSION FORCE PARALLEL DML PARALLEL degree-of-parallelism;
          ALTER SESSION FORCE PARALLEL QUERY PARALLEL degree-of-parallelism;
     f、调用过程start_redef_table启动在线重定义,如果启动失败,应调用ABORT_REDEF_TABLE先终止查找原因后再次启动
     g、从被重定义的表复制依赖对象到临时表(triggers, indexes, materialized view logs, grants, and constraints)及统计信息到临时表
     h、同步被重定义的表到临时表(调用过程sync_interim_table,此步可选)
     i、调用过程FINISH_REDEF_TABLE完成表的在线重订义 
     j、删除临时表

4、演示在线重定义

--下面基于主键来演示在线重定义
--环境
scott@USBO> select * from v$version where rownum<2;
 
BANNER
--------------------------------------------------------------------------------
Oracle Database 11g Enterprise Edition Release 11.2.0.1.0 - Production
 
--创建需要重定义的表
scott@USBO> create table tb_emp(empno number(4) not null,ename varchar2(10),
  2  job varchar2(10),hiredate varchar2(20),sal number(7,2),deptno number(2));
 
--下面为其添加相关约束
scott@USBO> alter table tb_emp add constraint pk_tb_emp primary key(empno);
 
scott@USBO> alter table tb_emp add constraint fk_tb_emp_dept_no foreign key(deptno) references dept(deptno);
 
--创建用于在线重定义的临时表
--注意,empno列可以为NULL,ename变化为name且长度增加,sal变为salary,数据精度发生变化,以及deptno使用了default
scott@USBO> create table tb_emp_int
  2  (empno number(4),name varchar2(20),hiredate varchar2(20),salary number,deptno number(2) default 30);
 
--基于重定义创建触发器
scott@USBO> CREATE OR REPLACE TRIGGER tr_bf_tb_emp_hiredate
  2     BEFORE UPDATE OF hiredate
  3     ON tb_emp
  4     FOR EACH ROW
  5  BEGIN
  6     :new.hiredate := TO_CHAR (SYSDATE, 'yyyymmdd hh24:mi:ss');
  7  END tr_bf_tb_emp_hiredate;
  8  /
 
Trigger created.
 
--基于中间表创建触发器
scott@USBO> CREATE OR REPLACE TRIGGER tr_bf_tb_emp_int_hiredate
  2     BEFORE UPDATE OF hiredate
  3     ON tb_emp_int
  4     FOR EACH ROW
  5  BEGIN
  6     :new.hiredate := TO_CHAR (SYSDATE + 10, 'yyyymmdd hh24:mi:ss');
  7  END tr_bf_tb_emp_int_hiredate;
  8  /
 
Trigger created.
 
--收集统计信息
scott@USBO> exec dbms_stats.gather_table_stats('SCOTT','TB_EMP',cascade=>true);
 
scott@USBO> exec dbms_stats.gather_table_stats('SCOTT','TB_EMP_INT',cascade=>true);
 
scott@USBO> select table_name,num_rows from user_tables where table_name like 'TB_EMP%';
 
TABLE_NAME                       NUM_ROWS
------------------------------ ----------
TB_EMP                                  0
TB_EMP_INT                              0
 
--对重定义表执行DML操作
--此时使用了dbms_lock.sleep (5),也就是整个操作完成需要500s,我们在这个期间实施重定义
scott@USBO> get ins_tb_emp.sql
  1  DECLARE
  2     v_deptno   NUMBER (2);
  3  BEGIN
  4     FOR i IN 1 .. 100
  5     LOOP
  6        IF MOD (i, 2) = 0
  7        THEN
  8           v_deptno := 10;
  9        ELSE
 10           v_deptno := 20;
 11        END IF;
 12        INSERT INTO tb_emp
 13           SELECT i,
 14                  'Name_' || TO_CHAR (i),
 15                  'Job_' || TO_CHAR (i),
 16                  TO_CHAR (SYSDATE, 'yyyymmdd hh24:mi:ss'),
 17                  i + 100,
 18                  v_deptno
 19             FROM DUAL;
 20        dbms_lock.sleep (5);
 21        COMMIT;
 22     END LOOP;
 23* END;
 24  /
 
--下面再开启一个新的session,用于在线重定义表 
scott@USBO> set serveroutput on;
--校验表能否被重定义
scott@USBO> exec dbms_redefinition.can_redef_table('SCOTT','TB_EMP');
 
--开始重定义
scott@USBO> exec dbms_redefinition.start_redef_table('SCOTT', 'TB_EMP', 'TB_EMP_INT',-
> 'EMPNO EMPNO, ENAME NAME, SAL*1.10 SALARY, HIREDATE HIREDATE,DEPTNO DEPTNO');
 
--可以从视图user_snapshots查询到临时表的信息
scott@USBO> select name,table_name,updatable,status from user_snapshots;
 
NAME                           TABLE_NAME                     UPD STATUS
------------------------------ ------------------------------ --- -------
TB_EMP_INT                     TB_EMP_INT                     NO  VALID
 
--查看表tb_emp_int,此时也有5条记录被插入
scott@USBO> select count(*) from tb_emp_int;
 
  COUNT(*)
----------
         5
 
--正在插入到tb_emp_int产生的日志信息,从6开始,实际上执行start_redef_table时前5条记录已经被复制到临时表        
scott@USBO> select * from mlog$_tb_emp;
 
     EMPNO SNAPTIME$$        D O CHANGE_VEC      XID$$
---------- ----------------- - - ---------- ----------
         6 40000101 00:00:00 I N FE         1.4074E+15
         7 40000101 00:00:00 I N FE         5.6299E+14
         8 40000101 00:00:00 I N FE         2.2519E+15
         9 40000101 00:00:00 I N FE         1.4075E+15
 
scott@USBO> select * from rupd$_tb_emp;
 
no rows selected
 
--检查约束等是否已经添加到临时表
scott@USBO> select constraint_name,table_name,status from user_constraints where table_name='TB_EMP_INT';
 
no rows selected
 
--注册依赖对象        
scott@USBO> exec dbms_redefinition.register_dependent_object('SCOTT', 'TB_EMP', 'TB_EMP_INT', -
> dbms_redefinition.cons_trigger, 'SCOTT', 'tr_bf_tb_emp_hiredate', 'tr_bf_tb_emp_int_hiredate');
 
PL/SQL procedure successfully completed.
 
scott@USBO> select constraint_name,table_name,status from user_constraints where table_name='TB_EMP_INT';
 
no rows selected
 
--将重定义表的依赖对象复制到临时表
scott@USBO> DECLARE       
  2     retval   NUMBER (5);
  3  BEGIN
  4     DBMS_REDEFINITION.copy_table_dependents ('SCOTT', 'TB_EMP', 'TB_EMP_INT', 0,
  5                                              copy_constraints   => TRUE,
  6                                              num_errors         => retval);
  7     DBMS_OUTPUT.put_line (retval);
  8  END;
  9  /
 
PL/SQL procedure successfully completed.
 
--查看临时表上的依赖对象,可以看到出现了以TMP$$开头的相关约束
scott@USBO> select constraint_name,table_name,status from user_constraints where table_name in('TB_EMP_INT','TB_EMP');
 
CONSTRAINT_NAME                TABLE_NAME                     STATUS
------------------------------ ------------------------------ --------
SYS_C0024681                   TB_EMP                         ENABLED
PK_TB_EMP                      TB_EMP                         ENABLED
FK_TB_EMP_DEPT_NO              TB_EMP                         ENABLED
TMP$$_SYS_C00246810            TB_EMP_INT                     ENABLED
TMP$$_PK_TB_EMP0               TB_EMP_INT                     ENABLED
TMP$$_FK_TB_EMP_DEPT_NO0       TB_EMP_INT                     DISABLED
 
--查看表tb_emp,此时重定义表插入了36条记录
scott@USBO> select count(*) from tb_emp;
 
  COUNT(*)
----------
        36
 
--临时表上的记录为5        
scott@USBO> select count(*) from tb_emp_int;
 
  COUNT(*)
----------
         5                       
 
--下面的过程用于同步重定义表与临时表
scott@USBO> exec dbms_redefinition.sync_interim_table('SCOTT', 'TB_EMP', 'TB_EMP_INT');
 
PL/SQL procedure successfully completed.           
 
--这是同步后的结果
scott@USBO> select count(*) from tb_emp_int;
 
  COUNT(*)
----------
        39
 
--最后完成在线重定义,此时如果重定义表上事务没有被完成,需要等到所有事务完成
scott@USBO> exec dbms_redefinition.finish_redef_table('SCOTT', 'TB_EMP', 'TB_EMP_INT');
 
PL/SQL procedure successfully completed.
 
scott@USBO> select count(*) from tb_emp_int;
 
  COUNT(*)
----------
       100       
 
--检验结果,可以看到列salary上的值发生了变化      
scott@USBO> select new.salary new_sal, old.sal old_sal,new.deptno new_deptno,old.deptno old_detpno
  2  from tb_emp new, tb_emp_int old
  3  where new.empno = old.empno and rownum<=3;
 
   NEW_SAL    OLD_SAL NEW_DEPTNO OLD_DETPNO
---------- ---------- ---------- ----------
     111.1        101         20         20
     112.2        102         10         10
     113.3        103         20         20
 
--验证触发器
scott@USBO> select table_name, trigger_name
  2  from user_triggers;
 
TABLE_NAME                     TRIGGER_NAME
------------------------------ ------------------------------
TB_EMP                         TR_BF_TB_EMP_HIREDATE
TB_EMP_INT                     TR_BF_TB_EMP_INT_HIREDATE
 
--Author : Leshami
--Blog   : http://blog.csdn.net/leshami
 
--此时临时表上的触发器被应用到重定义的表
scott@USBO> select trigger_body from user_triggers
  2  where table_name = 'TB_EMP';
 
TRIGGER_BODY
--------------------------------------------------------------------------------
BEGIN
   :new.hiredate := to_char(SYSDATE + 10,'yyyymmdd hh24:mi:ss');
END tr_bf_int_emp_hiredate;
 
scott@USBO> desc tb_emp
 Name                                                                                Null?    Type
 ----------------------------------------------------------------------------------- -------- --------------------
 EMPNO                                                                                        NUMBER(4)
 NAME                                                                                         VARCHAR2(20)
 HIREDATE                                                                                     VARCHAR2(20)
 SALARY                                                                                       NUMBER
 DEPTNO                                                                                       NUMBER(2)
 
 scott@USBO> select column_name,data_type, data_default from dba_tab_columns
  2  where owner='SCOTT' and table_name='TB_EMP' and column_name='DEPTNO';
 
Column Name          Data Type                 DATA_DEFAULT
-------------------- ------------------------- ------------------------------------------------------------------
DEPTNO               NUMBER                    30
 
 
--------------------- 
作者:Leshami 
来源:CSDN 
原文:https://blog.csdn.net/leshami/article/details/11760417 
版权声明:本文为博主原创文章,转载请附上博文链接!

猜你喜欢

转载自blog.csdn.net/viviliving/article/details/89465688