DBMS_REDEFINITION是Oracle 11g唯一真正支持业务几乎不停机的在线分区改造方案,需主键、双表同步与原子交换,但存在字段类型、权限及虚拟列等多重限制。

DBMS_REDEFINITION 是唯一真正在线的方案
Oracle 11g 不支持 ALTER TABLE ... MODIFY TO PARTITION(那是 12.2+ 的语法),必须用 DBMS_REDEFINITION 才能实现业务几乎不停机的改造。它靠双表同步+原子交换完成,但不是“无门槛”——CAN_REDEF_TABLE 成功只代表基础条件满足,后续仍可能卡在字段类型或权限上。
常见失败现象:ORA-12089: table does not have a primary key,但实际原因可能是含 LONG 字段、虚拟列未物化、或主键列上有函数索引;错误信息不提示具体哪一列违规。
- 原表必须有主键或唯一约束(可用
cons_use_rowid参数绕过,但要求表无LONG/BFILE/嵌套表) - 分区键必须是原表已存在的物理列,不能是
GENERATED ALWAYS AS虚拟列(哪怕你CREATE TABLE ... AS SELECT时显式物化了也不行) - 执行用户需显式授予
EXECUTE_CATALOG_ROLE和ALTER ANY TABLE,仅DBA角色默认不包含前者 - 中间表(interim table)必须手动建好,且结构、表空间、分区定义(含首分区)完全匹配目标形态,
COPY_TABLE_DEPENDENTS的copy_indexes参数建议设为0,索引单独建更可控
CTAS + RENAME 适合可停写窗口的场景
如果应用能接受几分钟停写,CREATE TABLE ... AS SELECT(CTAS)比在线重定义快得多,尤其配合 NOLOGGING PARALLEL(DEGREE 4)。但它本质是“重建”,不是“改造”,数据一致性全靠你控制写入窗口期。
典型错误:切换后查不到刚插入的记录——因为 INSERT INTO new_table SELECT * FROM old_table 和两次 RENAME 之间存在时间差,新写入的数据不会自动同步过去。
- 必须提前建好目标分区结构,例如
PARTITION BY RANGE(time_fee) INTERVAL(NUMTOYMINTERVAL(1,'MONTH')),且首分区不可省略(如PARTITION p1 VALUES LESS THAN (TO_DATE('2025-01-01','YYYY-MM-DD'))) -
NOLOGGING能提速,但要求表空间为非归档模式,或已启用FORCE LOGGING,否则会 silently 退回到 logging 模式 - 索引、约束、触发器全部要手动重建;
DBMS_METADATA.GET_DDL可导出原定义,但外键依赖顺序必须人工校验 - 切换前务必停写,或用应用层双写/数据库触发器兜底,否则数据丢失不可逆
EXPDP/IMPDP 是大表最稳的折中选择
当 CAN_REDEF_TABLE 失败又不愿冒险停写时,EXPDP + IMPDP 是 Oracle 11g 下最可靠的兜底方案。它逻辑导出再物理重建,中断时间取决于数据量,但通常控制在分钟级,且不挑字段类型。
容易踩的坑:DIRECTORY 参数必须填数据库内已创建的目录对象名(如 DATA_PUMP_DIR),填本地路径会报 ORA-39002;导入时若用 PARTITION_OPTIONS=DEPARTITION,目标表必须已存在且结构匹配,否则报 ORA-39151。
- 导出前确保原表无进行中的 DML(否则导出结果不一致),可用
FLASHBACK_SCN锁定一致性点 - 导入命令里加
TRANSFORM=SEGMENT_ATTRIBUTES:0可避免表空间继承问题,尤其跨库迁移时 -
IMPDP的REMAP_TABLE可直接映射到新分区表,无需先RENAME原表 - 索引和约束会在导入时自动重建,但外键依赖关系需检查
STATUS是否为ENABLED
分区键设计比迁移手段更关键
很多团队花大力气做完迁移,发现查询没变快、归档删不掉——问题往往出在分区键选错了。Oracle 11g 的分区裁剪(partition pruning)高度依赖谓词是否命中分区键,且无法智能改写 SQL。
比如用日期字段却选 LIST 分区,每月都得手工 ALTER TABLE ADD PARTITION;用高基数字段(如 ORDER_ID)做 RANGE 分区,会导致数据严重倾斜,某些分区占 80% 数据量。
- 时间类字段优先用
RANGE+INTERVAL(月/年),避免手工维护分区 - 地区/状态等低基数字段用
LIST,但必须预估值域范围,新增值需提前ADD或用DEFAULT分区兜底 - 复合分区(如
RANGE-LIST)在 11g 支持,但管理复杂度陡增,除非明确需要两级裁剪 - 分区数不宜过多(一般 ≤ 64),否则
ALL_TAB_PARTITIONS查询本身成瓶颈
真正难的从来不是怎么切,而是切完之后,SQL 是否真能走分区裁剪、历史分区能否被快速 DROP 或 EXCHANGE 出去——这些得在建表前就验证好执行计划和运维脚本。


















