Oracle在线重定义介绍
• Oracle 在线重定义(Online Redefinition)是一种功能,允许数据库管理员在不需要停
止或显著影响数据库正常操作的情况下,对数据库表进行结构化修改。
• 这个功能在大型企业级数据库环境中非常重要,因为停机时间可能导致业务中断和收入
损失。
• Oracle 在线重定义功能通过`DBMS_REDEFINITION` 包提供,允许在表数据仍在被查询
和修改的同时,对表进行复杂的结构化修改,如添加或删除列、重组表空间、重新定义
分区等。
可以实现的功能:
•将表移动到其它表空间
•增加、修改或者删除表的字段
•将非分区表转换为分区表
•修改表的分区结构
•减少表的碎片
•将普通表转换为索引组织表
以下限制适用于在线重新定义表
◼ 主键或伪主键要求:
➢ 如果使用主键或伪主键(所有列都有非空约束的唯一键或约束)重新定义,则重新定义后的表必须保留相同的主键或伪主键
列。
➢ 如果使用行ID(rowid)重新定义,则表不能是索引组织表(index-organized table)。
◼ 物化视图日志:
➢ 重新定义具有物化视图日志的表后,任何依赖的物化视图在随后的刷新中必须进行完全刷新。
◼ 复制限制:
➢ n-way master configuration中的表可以重新定义。
➢ 不允许水平子集(表中行的子集)、垂直子集(表中列的子集)和列转换进行在线重定义
◼ 溢出表限制:
➢ 索引组织表的溢出表不能单独在线重新定义。
◼ 细粒度访问控制限制:
➢ 具有细粒度访问控制(行级安全性)的表不能在线重新定义。
◼ 闪回数据归档限制:
➢ 启用了闪回数据归档的表不能在线重新定义。
➢ 不能为中间表启用闪回数据归档。
◼ BFILE列限制:
➢ 具有BFILE列的表不能在线重新定义。
◼ LONG和LONG RAW列要求:
➢ 具有LONG列的表可以在线重新定义,但这些列必须转换为CLOB。
➢ LONG RAW列必须转换为BLOB。
➢ 具有LOB列的表是可接受的。
◼ 系统架构限制:
➢ SYS和SYSTEM架构中的表不能在线重新定义。
◼ 临时表限制:
➢ 临时表不能重新定义。
◼ 行子集限制:
➢ 表中的行子集不能重新定义。
◼ 列映射限制:
➢ 在将中间表的列映射到原始表时,只能使用简单的确定性表达式、序列和SYSDATE。例如,不允许使用子查询。
◼ 新列的非空约束:
➢ 如果在重新定义过程中添加了新列且这些列没有列映射,则在重新定义完成之前,这些列不能声明为非空。
◼ 参照约束限制:
➢ 被重新定义的表与中间表之间不能有任何参照约束。
◼ 日志记录限制:
➢ 表重新定义不能以NOLOGGING方式进行。
◼ 物化视图日志和队列表的限制:
➢ 对于物化视图日志和队列表,在线重新定义仅限于物理属性的更改。不允许水平或垂直子集,也不允许任何列转换。列映射字符串的唯一有效值
是NULL。
◼ 分区表限制:
➢ 如果分区表包含一个或多个嵌套表,则不能对该表进行在线重新定义。
◼ VARRAY和嵌套表转换:
➢ 可以使用CAST操作符在列映射中将VARRAY转换为嵌套表。但不能将嵌套表转换为VARRAY。
◼ 顺序列映射限制:
➢ 当DBMS_REDEFINITION.START_REDEF_TABLE过程的col_mapping参数中的列包含序列时,orderby_cols参数必须为NULL。
◼ 多表会话限制:
➢ 如果表通过引用分区相关联,则不能在不同的DBMS_REDEFINITION会话中同时对多个表运行在线重新定义。
◼ 对象表或XMLType表的限制:
➢ 如果其他表有引用被重新定义表的REF列,在线重新定义对象表或XMLType表可能会导致其他表中的REF悬空。
https://docs.oracle.com/cd/E11882_01/server.112/e25494/tables.htm#i1006754
总体工作过程
◼ 选择执行方式:主键还是ROWID
◼ 检查选择的表是否可以执行在线重定义: dbms_redefinition.can_redef_table
◼ 按照要求创建空的中间表
◼ 开始进行在线重定义dbms_redefinition.start_redef_table
◼ 复制依赖对象(自动或者手动)
◼ 同步表的数据dbms_redefinition.sync_interim_table
◼ 完成在线重定义dbms_redefinition.finish_redef_table
需要注意的细节
◼ 选择执行方式:主键还是ROWID
➢ 主键:如果表上有主键,那么首选的方式是主键
➢ ROWID:如果表上没有主键,那么只能使用ROWID方式,此时需要注意以下几点:
✓ 索引索引表不能使用rowid方式
✓ Rowid方式会在最终表上生成一个名称为M_ROW$$的隐藏字段
✓ 在完成在线重定义之后来,可以使用ALTER TABLE ... DROP UNUSED COLUMNS来删除这个字段
◼ 创建空的中间表,有两种选择:
➢ 只创建空表,原表所有对象使用自动复制的方式
➢ 手动按照原表建立所有对象,并且将中间表的对象与原表的对象一对一进行注册
另外,中间表最终会替换原表,因此中间表需要按照期望的结构来进行创建,比如字段数量,
字段类型,分区等等。
◼ 复制依赖对象(自动或者手动):
➢ 使用自动方式,需要建立一个空的中间表,不要建任何表的相关对象,由
dbms_redefinition.copy_table_dependents 这个过程自动建立中间表的相关对象,并且一对一地与原表进行关
联注册,最后在将中间表切换为原表时,所有对象都会改名,与原表的对象名称一致。
➢ 使用手动方式,在建立中间表时,需要手动按照原表建立所有对象,并且将中间表的对象与原表的对象使用
dbms_redefinition.register_dependent_object 过程进行一对一地关联注册,注册过的对象,在最后切换为原表
时会自动改名,与原表的对象名称一致。
◼ 同步表的数据dbms_redefinition.sync_interim_table :
➢ 这个过程是可以多次重复执行的
➢ 如果表比较大,可以在平时业务不忙时定期发起表的数据同步,这样可以减少最后将中间表切
换为原表的时间窗口。
◼ 完成在线重定义dbms_redefinition.finish_redef_table :
➢ 这个过程会有一个短暂的锁表操作
➢ Finish操作会等待所有DML操作提交,然后才去锁表,完成中间表到原表的切换
➢ 如果使用rowid方式,最终表会出现一个名为M_ROW$$的隐藏字段,可以使用ALTER TABLE...
DROP UNUSED COLUMNS 语句删除这个隐藏字段
➢ 这个隐藏字段不会重复增加,即使这张表经过10次在线重定义,也只会有一个M_ROW$$ 的隐藏
字段
在线重定义的结果
◼ 原始表重新定义
➢ 原始表将被重新定义,包含中间表的列、索引、约束、授权、触发器和统计信息
◼ 依赖对象重命名
➢ 注册的依赖对象(通过REGISTER_DEPENDENT_OBJECT显式注册或通过COPY_TABLE_DEPENDENTS隐式注
册)将自动重命名,以便重新定义后的表上的依赖对象名称与重新定义前相同。
➢ 涉及中间表的参照约束现在涉及重新定义后的表,并且这些约束已启用。
◼ 转移与删除
➢ 重新定义前定义在原始表上的任何索引、触发器、物化视图日志、授权和约束都将被转移到中间表,并在用户
删除中间表时被删除。
➢ 重新定义前涉及原始表的任何参照约束现在涉及中间表,并且这些约束已禁用。
◼ 对象失效
➢ 一些PL/SQL对象、视图、同义词和其他表依赖对象可能会失效。
➢ 只有那些依赖于表中已更改元素的对象会失效。
Oracle在线重定义使用场景,下面是一个具体的案例
原表
drop table SCOTT.test01; create table SCOTT.test01( empno number(7), ename varchar2(64) not null, job varchar2(9), mgr number(7), hiredate date, sal number(7), comm number(7), deptno number(3), constraint pk_empno primary key(empno) ); create index SCOTT.ind_test01 on SCOTT.test01(deptno); begin for i in 1..500000 loop insert into SCOTT.test01 values( i,dbms_random.string('p',64),dbms_random.string('u',9),i,sysdate,i,i,mod(i,999)); end loop; commit; end; /
中间表
drop table SCOTT.test01_int; create table SCOTT.test01_int( empno number(7), ename varchar2(64) not null, job varchar2(9), mgr number(7), hiredate date, sal number(7), comm number(7), deptno number(3), constraint pk_empno_int primary key(empno) ) partition by hash (ename) partitions 100; create index SCOTT.ind_test01_int on SCOTT.test01_int(deptno);
在线重定义执行过程
SQL> exec DBMS_REDEFINITION.CAN_REDEF_TABLE('SCOTT','TEST01',DBMS_REDEFINITION.CONS_USE_PK); SQL> exec DBMS_REDEFINITION.START_REDEF_TABLE('SCOTT','TEST01','TEST01_INT'); PL/SQL procedure successfully completed. SQL> exec DBMS_REDEFINITION.SYNC_INTERIM_TABLE('SCOTT','TEST01','TEST01_INT'); PL/SQL procedure successfully completed. SQL> exec DBMS_REDEFINITION.FINISH_REDEF_TABLE('SCOTT','TEST01','TEST01_INT'); PL/SQL procedure successfully completed.
结果:
SQL> set linesize 1000 SQL> col table_name for a20 SQL> col PARTITION_NAME for a20 SQL> select table_name,partition_name,num_rows from dba_tab_partitions where table_name='TEST01';
对表增加一个字段
-- 原表 drop table SCOTT.test01; create table SCOTT.test01( empno number(7), ename varchar2(64) not null, job varchar2(9), mgr number(7), hiredate date, sal number(7), comm number(7), deptno number(3), constraint pk_empno primary key(empno) ); create index SCOTT.ind_test01 on SCOTT.test01(deptno); begin for i in 1..500000 loop insert into SCOTT.test01 values( i,dbms_random.string('p',64),dbms_random.string('u',9),i,sysdate,i,i,mod(i,999)); end loop; commit; end; / --中间表 drop table SCOTT.test01_int; create table SCOTT.test01_int( empno number(7), ename varchar2(64) not null, job varchar2(9), mgr number(7), hiredate date, sal number(7), comm number(7), deptno number(3), add_column varchar2(64), constraint pk_empno_int primary key(empno) ); create index SCOTT.ind_test01_int on SCOTT.test01_int(deptno); begin DBMS_REDEFINITION.CAN_REDEF_TABLE('SCOTT','TEST01'); end; / begin DBMS_REDEFINITION.START_REDEF_TABLE(uname=>'SCOTT', options_flag=>dbms_redefinition.cons_use_rowid, orig_table=>'TEST01', int_table=>'TEST01_INT'); end; / PL/SQL procedure successfully completed. begin DBMS_REDEFINITION.SYNC_INTERIM_TABLE('SCOTT','TEST01','TEST01_INT'); end; / PL/SQL procedure successfully completed. begin DBMS_REDEFINITION.FINISH_REDEF_TABLE('SCOTT','TEST01','TEST01_INT'); end; /
Oracle在线重定义经验总结
经验一:2351452.1,DBMS_REDEFINITION.FINISH_REDEF_TABLE会执行flush share pool
◼ DBMS_REDEFINITION.FINISH_REDEF_TABLE 阶段,底层会执行flush share pool 的动作
◼ From 11.1 Online Redefinition May Flush Shared Pool When Execute
DBMS_REDEFINITION.FINISH_REDEF_TABLE (Doc ID 2351452.1)
◼ 按照文档,这是预期行为
◼ 规避方法
➢ alter session set events '10995 trace name context forever, level 2';
经验二:DBMS_REDEFINITION.ABORT_REDEF_TABLE是否对原表造成影响
◼ DBMS_REDEFINITION.ABORT_REDEF_TABLE 中途取消在线重定义
➢ abort 过程会执行一些列的底层操作,其中包括truncate 中间表的操作
➢ alter table "SCOTT"."EMP_INT" drop (m_row$$)
➢ drop table "SCOTT"."MLOG$_EMP" purge
➢ truncate table "SCOTT"."EMP_INT"
◼ 所以结论是abort不会对原表造成影响
➢ abort 方式取消在线重定义后,建议将中间表drop
经验三:新表的统计信息怎样与原表保持一致
◼ 使用copy_statistics方式
➢ 在DBMS_REDEFINITION.COPY_TABLE_DEPENDENTS 阶段
➢ 指定copy_statistics = TRUE
➢ 此时统计信息会由在线重定义复制到中间表
◼ 在线重定义完成后,手动更新统计信息
➢ 设置参数copy_statistics = FALSE
➢ 然后手动收集统计信息
经验四:对大表或超大表进行在线重定义,如何提高速度
◼ 在重定义开始阶段,在session 级别配置并行
➢ alter session force parallel dml parallel N;
➢ alter session force parallel ddl parallel N;
➢ alter session force parallel query parallel N;
◼ 注意事项
➢ 多次测试发现,在sync 和finish 阶段需要session noparallel
➢ 否则sync 和finish 的执行时间会明显变长