在线重定义(Online Redefinition)是Oracle提供的一项高级特性,它允许DBA在表持续对外提供服务、不断有DML操作的情况下,对表结构进行深度改造。传统的ALTER TABLE操作在涉及数据重组时往往需要长时间持有锁,对于交易型核心表来说风险极高,而在线重定义把这个过程拆成了"后台拷贝数据+短暂切换"两个阶段,切换时刻只需要秒级的排他锁,业务几乎无感知。本文将从原理、步骤和实战细节三个层面完整讲解这项技术。

一、什么场景适合使用在线重定义
在线重定义能完成的改造远不止加列删列这么简单。它本质上是在目标表上重建一份新结构,然后通过增量同步保持两边一致,因此几乎所有"需要重建表"的需求都可以用它来无感完成。常见的适用场景包括以下几类。
第一类是普通表改造为分区表。这是在线重定义最经典的应用,当一个业务表数据量增长到数亿行,查询和归档压力变大时,可以在线把它重定义为按范围或列表分区的分区表,整个过程业务不中断。第二类是表空间迁移,比如需要把表从本地管理表空间迁移到ASSM表空间,或者把数据文件挪到更高性能的存储上。第三类是结构调整,包括修改列的数据类型(例如VARCHAR2改为NUMBER)、改变列顺序、重建碎片化严重的表以回收空间、修改PCTFREE等存储参数、变更主键或去掉压缩属性等。
需要注意的是,并非所有表都能直接做在线重定义。目标表必须具备主键,或者选择基于ROWID的模式;如果表上存在物化视图日志、是物化视图容器表、属于索引组织表的某些特殊形态,都可能受限。因此在动手之前,务必先调用CAN_REDEF_TABLE过程做可行性检查。
二、在线重定义的执行步骤详解
整个流程分为五步:检查可行性、创建中间表、启动重定义、同步数据、完成切换。下面以一张名为ORDERS的业务表为例,将它重定义为按月分区的分区表。假设连接用户是BIZ,需要先授予执行DBMS_REDEFINITION的权限。
第一步先做环境准备和检查。用具备DBA角色的用户执行授权,然后验证源表是否满足在线重定义条件:
-- 授予执行权限
GRANT EXECUTE ON DBMS_REDEFINITION TO biz;
GRANT CREATE ANY TABLE, ALTER ANY TABLE, DROP ANY TABLE, LOCK ANY TABLE TO biz;
GRANT SELECT ANY TABLE TO biz;
-- 连接到业务用户后检查表是否可重定义
BEGIN
DBMS_REDEFINITION.CAN_REDEF_TABLE(
uname => 'BIZ',
tname => 'ORDERS',
options_flag => DBMS_REDEFINITION.CONS_USE_PK
);
END;
/检查通过后,第二步创建中间表。中间表就是你期望的最终结构,这里定义成按创建月份分区的分区表,列定义要与源表保持兼容:
CREATE TABLE biz.orders_interim ( order_id NUMBER NOT NULL, customer_id NUMBER, amount NUMBER(12,2), order_date DATE, status VARCHAR2(20), CONSTRAINT pk_orders_interim PRIMARY KEY (order_id) ) PARTITION BY RANGE (order_date) ( PARTITION p202401 VALUES LESS THAN (DATE '2024-02-01'), PARTITION p202402 VALUES LESS THAN (DATE '2024-03-01'), PARTITION p202403 VALUES LESS THAN (DATE '2024-04-01'), PARTITION pmax VALUES LESS THAN (MAXVALUE) );
第三步启动重定义任务,这是耗时最长的阶段。如果希望并行加速,可以先在会话中开启并行度,源表和中间表也可以设置并行属性。这一步执行期间,源表仍然可以被正常读写,Oracle会自动在源表上创建物化视图日志来记录增量变化:
ALTER SESSION FORCE PARALLEL DML PARALLEL 8;
ALTER SESSION FORCE PARALLEL QUERY PARALLEL 8;
BEGIN
DBMS_REDEFINITION.START_REDEF_TABLE(
uname => 'BIZ',
orig_table => 'ORDERS',
int_table => 'ORDERS_INTERIM',
col_mapping => NULL, -- 列名一致时可省略映射
options_flag => DBMS_REDEFINITION.CCONS_USE_PK
);
END;
/第四步是可选的同步操作。由于数据拷贝期间业务仍在写入,拷贝完成后两边存在差异。FINISH_REDEF_TABLE内部会做一次最终同步,但如果拷贝持续了几个小时,业务写入量很大,建议在正式切换前手动执行一到两次SYNC_INTERIM_TABLE,把差异提前抹平,这样最终切换的锁定时间会更短:
-- 手动同步,可多次执行
BEGIN
DBMS_REDEFINITION.SYNC_INTERIM_TABLE(
uname => 'BIZ',
orig_table => 'ORDERS',
int_table => 'ORDERS_INTERIM'
);
END;
/第五步执行完成切换。这一步会短暂锁定源表,应用最后一次增量数据,然后把两张表的定义互换——源表名指向新的分区结构,中间表名持有旧表的定义,整个过程通常在几秒内完成。切换后记得重命名或重建中间表上的索引约束命名,并将新表及其分区的统计信息收集一遍:
BEGIN
DBMS_REDEFINITION.FINISH_REDEF_TABLE(
uname => 'BIZ',
orig_table => 'ORDERS',
int_table => 'ORDERS_INTERIM'
);
END;
/
-- 切换完成后收集统计信息
EXEC DBMS_STATS.GATHER_TABLE_STATS('BIZ','ORDERS',CASCADE => TRUE);三、常见问题与注意事项
首先是索引和约束的处理。START_REDEF_TABLE只会搬运数据,源表上的索引、约束、触发器、授权和统计信息并不会自动迁移到新结构上。推荐的做法是:在启动重定义之后、执行FINISH之前,在中间表上手工创建与源表一致的索引和约束,并用COPY_TABLE_DEPENDENTS过程自动复制依赖对象,这样切换瞬间业务不会因为缺少索引而性能抖动:
DECLARE
error_count PLS_INTEGER := 0;
BEGIN
DBMS_REDEFINITION.COPY_TABLE_DEPENDENTS(
uname => 'BIZ',
orig_table => 'ORDERS',
int_table => 'ORDERS_INTERIM',
copy_indexes => DBMS_REDEFINITION.CONS_ORIG_PARAMS,
copy_triggers => TRUE,
copy_constraints=> TRUE,
ignore_errors => TRUE,
num_errors => error_count
);
DBMS_OUTPUT.PUT_LINE('复制过程中的错误数: ' || error_count);
END;
/其次是空间和日志问题。在线重定义相当于把表完整复制一份,需要预留至少一倍表大小的额外空间,同时会产生大量归档日志,如果数据库运行在归档模式,务必确认归档空间充足,避免撑爆磁盘导致数据库挂起。对于超大表,建议安排在业务低峰期启动START_REDEF_TABLE,并开启并行来缩短拷贝窗口。
再来看常见报错。ORA-12089表示表上已存在物化视图日志,需要先删除旧的日志再重试;ORA-12091通常是因为存在未完成的物化视图快速刷新依赖该表,可以先完成刷新或删除相关物化视图;ORA-23539和ORA-23540则说明重定义会话异常中断后残留了未清理的状态,此时需要调用ABORT_REDEF_TABLE终止本次任务,再重新开始。另外如果FINISH阶段长时间无法获得锁,多半是长事务占着源表,需要与应用侧协调kill相关会话后再执行。
最后提醒一点,主键模式和ROWID模式的选择。CONS_USE_PK基于主键同步,性能好且是首选;CONS_USE_ROWID适用于没有主键又暂时不方便加主键的表,但同步效率较低,而且切换后表中会多出一个隐藏的ROWID列。生产环境操作前,建议先在测试环境完整演练一遍流程,确认切换时间和依赖对象处理无误后再正式实施。
Oracle在线重定义DBMS_REDEFINITION表结构重构修改时间:2026-09-01 23:20:36