DBMS_REDEFINITION是唯一支持在线将非分区表转为分区表的方案,因其采用双表并行同步机制,在用户持续读写时完成数据迁移与原子切换,但需满足主键约束、列类型兼容、权限充足等硬性条件。
为什么 DBMS_REDEFINITION 是唯一靠谱的在线方案
因为 oracle 不允许直接把非分区表改成分区表,alter table ... partition by ... 会报 ora-14006: invalid partition name。重建表(create table as select + 交换)又必须锁表,业务扛不住。只有 dbms_redefinition 能在用户持续读写的同时完成转换——前提是满足几个硬性条件。
它本质是“双表并行同步”:建一个结构正确的新分区表,把原表数据和 DML 变更逐步搬过去,最后原子切换。不是魔法,但够用。
- 原表必须有主键或唯一约束(否则无法精确捕获变更)
- 不能有 LONG、BFILE、嵌套表等不支持重定义的列类型
- 目标分区表的分区键必须是原表已有列,且不能是虚拟列(除非已物化)
- 执行用户需有
EXECUTE_CATALOG_ROLE和对两张表的SELECT、INSERT、UPDATE、DELETE权限
分三步走:校验 → 同步 → 切换
整个流程分三个明确阶段,每步失败都可回退,但别跳过 CAN_REDEF_TABLE 校验——它不报错不代表真能跑通,比如某些索引组织表或带物化视图日志的表会静默失败。
-
校验阶段:运行
DBMS_REDEFINITION.CAN_REDEF_TABLE,传入原表名、新表名、选项dbms_redefinition.cons_use_pk(推荐用主键,比 rowid 更稳) -
同步阶段:调
START_REDEF_TABLE建中间表 →SYNC_INTERIM_TABLE拉初始数据 →COPY_TABLE_DEPENDENTS搬索引/约束/触发器(注意copy_indexes参数设为0手动建分区索引更可控) - 切换阶段:最后一次
SYNC_INTERIM_TABLE后,立刻FINISH_REDEF_TABLE——这步毫秒级完成,但会短暂加SX锁,DML 等待时间取决于未同步的变更量
分区键选错或数据倾斜导致性能雪崩
很多人只盯着语法,忽略分区设计本身。用日期字段分区却按 LIST 分(如按年份枚举),新增分区要手动 ALTER TABLE ADD PARTITION;或者用高基数字段(如 user_id)做 RANGE 分区,结果每个分区数据量差 10 倍,查询走分区裁剪后仍扫大表。
- 时间类字段优先选
RANGE,配合INTERVAL自动扩展(Oracle 11g+) - 离散值少的字段(如
status、region_code)用LIST,但提前规划好未来值范围 - 避免用
HASH分区除非明确需要均匀分布——它不支持分区裁剪,等于放弃分区核心价值 - 建目标表时,
PARTITION BY RANGE (create_time) INTERVAL (NUMTOYMINTERVAL(1,'MONTH'))比手写 24 个PARTITION安全得多
常见卡点:ORA-12091、索引失效、统计信息丢失
最常卡在 FINISH_REDEF_TABLE 报 ORA-12091: cannot online redefine table with materialized view log——说明原表挂了物化视图日志,得先 DROP MATERIALIZED VIEW LOG ON t1。另外,重定义后原表变同义词指向新表,但索引名、约束名不会自动更新,应用里如果硬编码了索引名(比如 hint 里写 INDEX(t1 idx_old)),就会失效。
-
COPY_TABLE_DEPENDENTS的num_errors输出非 0 时,查DBA_REDEFINITION_ERRORS表看具体哪条约束/索引没搬成功 - 切换完成后,
DBMS_STATS.GATHER_TABLE_STATS必须重跑,否则优化器可能沿用旧统计信息,执行计划错乱 - 外键引用该表的其他表,其约束状态不变,但依赖的列若被重命名(比如分区键列名不同),会报
ORA-02270 - 全程别碰原表结构:加列、删列、改类型都会让
SYNC_INTERIM_TABLE失败
真正麻烦的从来不是命令敲不对,而是分区策略和线上数据分布不匹配。上线前拿 1% 数据做全链路压测,比反复调参有用得多。










