dbms_redefinition是oracle 19c中唯一支持在线将非分区表转为分区表的方案;它通过双表并行同步实现业务持续读写,但需满足主键约束、无虚拟列作分区键、中间表结构严格一致等硬性条件。

Oracle 19c 中无法用 ALTER TABLE ... MODIFY PARTITION BY 直接转换非分区表为分区表——该语法在 12.2+ 仅支持**已分区表调整分区策略**(如从 RANGE 改为 INTERVAL),对堆表会报 ORA-14006: invalid partition name。真正能实现“无缝”(即业务持续读写、零停机)的方案,只有 DBMS_REDEFINITION。
为什么 CAN_REDEF_TABLE 成功不代表真能跑通
很多人执行 DBMS_REDEFINITION.CAN_REDEF_TABLE 返回成功就直接开干,结果卡在 START_REDEF_TABLE 或中途失败。它只做基础检查,不验证以下真实拦路虎:
- 原表有未物化的虚拟列(
GENERATED ALWAYS AS),且目标分区键依赖它 → 重定义会静默失败 - 表上有物化视图日志(
MV log)→CAN_REDEF_TABLE不报错,但START_REDEF_TABLE会抛ORA-12091: cannot online redefine table with materialized view log - 使用
CONS_USE_ROWID选项时,原表含 LONG、BFILE、嵌套表 → 同步阶段崩溃,错误信息模糊 - 目标中间表的分区键列名大小写与原表不一致(如原表是
ORDER_DATE,中间表写成order_date)→SYNC_INTERIM_TABLE报ORA-14097: column type or size mismatch
中间表建表时必须避开的三个硬坑
建 interim table 看似简单,但错一个参数,后续全白忙:
- 不能提前创建任何索引、约束、触发器——
COPY_TABLE_DEPENDENTS会接管,手动建会导致名称冲突或 DDL 失败 - 分区键必须是原表**已存在且非虚拟**的列;若要用
TOTAL_AMOUNT(虚拟列),先ALTER TABLE ... MODIFY TOTAL_AMOUNT NUMBER(14,2) NOT NULL物化它 - 显式指定
TABLESPACE,否则默认走用户 default tablespace,可能和原表不在同一磁盘组,引发 I/O 偏斜 - 如果原表有基于函数的索引(如
CREATE INDEX idx ON t(UPPER(name))),COPY_TABLE_DEPENDENTS默认不复制,需事后手工重建
FINISH_REDEF_TABLE 那一秒到底锁什么
FINISH_REDEF_TABLE 确实是原子切换,但不是“完全无感”。它会持有一个短暂的 SX(Share eXclusive)锁,影响范围是:
- 所有正在执行的 DML(INSERT/UPDATE/DELETE)会阻塞,直到切换完成
- 阻塞时长 = 未同步的变更量 ÷ 当前系统吞吐能力;若之前没做多次
SYNC_INTERIM_TABLE,这一步可能卡数秒甚至分钟 - SELECT 不受影响,但正在执行的长查询若持有数据块锁,也可能被 SX 锁短暂等待
- 切换后,原表名指向新分区表,旧表被重命名为
INT$_<orig></orig>(可查USER_OBJECTS),务必确认清理
分区设计不当比语法错误更致命
语法全对、流程全过,上线后查询变慢十倍?大概率栽在分区设计上:
- 用高基数字段(如
CUSTOMER_ID)做HASH分区 → 所有查询无法分区裁剪,等价于全表扫描 - 按
ORDER_STATUS(取值仅 'PENDING','SHIPPED','CANCELLED')做RANGE分区 → 语法合法但逻辑荒谬,VALUES LESS THAN根本不适用 - 时间字段用
LIST分区(如枚举年份 '2024','2025','2026')→ 每年都要人工ALTER TABLE ADD PARTITION,运维雪崩 - 正确姿势:
PARTITION BY RANGE (ORDER_DATE) INTERVAL (NUMTOYMINTERVAL(1,'MONTH')),自动按月扩,无需人工干预
真正难的从来不是敲完那几行 DBMS_REDEFINITION 调用,而是校验条件是否真实满足、中间表结构是否严丝合缝、以及分区策略是否贴合业务生命周期。漏掉任意一环,都可能在 FINISH 那一刻才暴露问题。











