Oracle普通表改造分区表的方法总结

Oracle普通表改造分区表的方法总结

本文章主要讲述alter table modify改造方法,在线重定义改造方式请参考文章《Oracle表在线重定义操作总结(DBMS_REDEFINITION)》,CTAS方式请参考文章《Oracle CTAS+rename 表重建方法操作总结》

一、改造方法总结

1、DBMS_REDEFINITION在线重定义方式

2、create table nologging parallel as select /*+ parallel(a 4) */ 并发快速创建新表(分区形式),然后表名互换

3、expdp/impdp方法,非在线的改造方式

4、直接alter table xxx modify 方式将非分区表改为分区表,12.2以上版本才有的功能。

5、将表交换为一个分区表中的一个分区,然后split这个分区,然后表名互换

二、五种改造方法对比

方法 核心原理 优点 缺点 核心要点与注意事项
方法一:DBMS_REDEFINITION 在线重定义 创建一个新的分区表(中间表),通过内置包将源表数据在线拷贝至中间表,并在切换时通过原子操作交换表名。 真正的在线操作 ,业务几乎无感知; 支持回退 (ABORT_REDEF_TABLE); 可多次同步 (SYNC_INTERIM_TABLE) 控制切换时间; 支持列映射、列转换。 需要额外1倍表空间的临时表空间; 源表必须有主键或可用ROWID; 操作步骤较多,复杂度高; 主键列不能修改; 无法采用 NOLOGGING; 完成后必须手动收集统计信息。 需注意 : • 必须在同一用户下进行; • SYS / SYSTEM 用户下的表不支持; • 不支持含 LONGBFILE、域索引的表; • 不支持含物化视图日志的表(需先删除); • 重定义期间禁止 FLASHBACK TABLE/QUERY; • 表列的默认值需提前在中间表手工创建。
方法二:CREATE TABLE ... AS SELECT (CTAS) 利用 CREATE TABLE ... NOLOGGING PARALLEL AS SELECT 并发创建一个新的分区表,然后通过表名互换完成切换。 创建速度快,NOLOGGING + PARALLEL 产生的日志最少; 语法简单,易于理解; 不受主键、物化视图日志等限制。 业务需停止 (不能有DML); 必须先将表设为只读ALTER TABLE ... READ ONLY); 所有依赖对象(索引、约束、触发器、权限、视图、存储过程等)需全部手工重建 ,工作量大且容易遗漏; 完成后需手动收集统计信息。 建议 : • 在11g及以上版本可设置表为只读; • 建议将重建依赖对象的脚本提前准备好并测试通过; • 适合在维护窗口进行。
方法三:数据泵 (expdp/impdp) 通过 expdp 导出源表数据,再通过 impdp 导入到一个预先创建好的分区表中。 速度相对较快; 语法简单; 不受主键、物化视图日志等限制。 业务需停止 (不能有DML); 必须先将表设为只读 ; 所有依赖对象需全部手工重建 ,工作量大; 完成后需手动收集统计信息。 建议 : • 适合数据量极大的场景; • 可配合 NETWORK_LINK 参数实现远程迁移; • 与方法二类似,属于非在线方式。
方法四:ALTER TABLE ... MODIFY (12.2+) Oracle 12.2 及以上版本提供的原生在线DDL,直接将普通表转换为分区表,并可选择转换时对索引的处理方式。 语法最简洁 ,一条DDL完成; 依赖对象(索引、约束、触发器、默认值、权限、视图等)全部自动保留无需额外表空间 ; 支持 ONLINE 模式,业务几乎无感知。 仅限 Oracle 12.2+ ; 不支持域索引; 不支持含物化视图日志的表; 大表会产生大量 REDO 日志(10G以上大表需谨慎评估 ); 转换后需手动收集统计信息。 关键技巧 : • 建议提前删除非必要索引,转换后再重建,以减少REDO并加快速度; • 参考分区(Reference Partitioning)的在线转换需 19.12+; • 内部仍需移动数据,大表需充分测试。
方法五:EXCHANGE + SPLIT 分区交换 创建一个单分区表,通过 EXCHANGE PARTITION 与普通表交换(仅改数据字典),再通过 SPLIT PARTITION 将这个大分区拆分为多个分区。 EXCHANGE 操作极快(仅修改数据字典,不物理移动数据); 不需要额外的数据复制。 SPLIT 大分区时耗时非常长(需移动数据); 所有依赖对象需全部手工重建 ,工作量大; 操作步骤多,容易出错; 转换后需手动收集统计信息。 现状 : • 此方法步骤繁琐且存在性能瓶颈; • 如今已基本不使用,已被方法一或方法四取代。

三、改造方法选择推荐

推荐场景 推荐方法
生产环境,业务不能停,且数据库版本在 12.2+ 方法四 (ALTER TABLE ... MODIFY ONLINE) --- 首选,最省心
生产环境,业务不能停,但数据库版本低于 12.2 方法一 (DBMS_REDEFINITION) --- 唯一可靠的在线方式
有维护窗口,业务可停,表数据量巨大 (TB级) 方法二 (CTAS) 或 方法三 (expdp/impdp) --- 速度最快,配合 NOLOGGING + 并行,日志最少
追求操作最简,版本满足要求,表数据量中等 方法四 --- 一条命令搞定所有依赖对象
临时测试环境,快速验证分区效果 方法二 (CTAS) --- 最直接方便

四、alter table modify改造语法

ALTER TABLE ... MODIFY 命令是从 Oracle 12c R2(12.2) 版本开始引入的特性,用于直接将一个普通的堆组织表(heap-organized table)转换为分区表

特别注意:对于大表,建议提前删除非必要索引,转换后重建,减少redo量,加快整体速度

复制代码
ALTER TABLE table_name MODIFY
    PARTITION BY { 
        RANGE (column_list) [ INTERVAL (expr) ] ( partition_definition [, partition_definition ]... ) |
        LIST (column) ( partition_definition [, partition_definition ]... ) |
        HASH (column) { PARTITIONS num | ( partition_definition [, partition_definition ]... ) }
    }
    [ SUBPARTITION BY ... ] -- 可选的子分区定义
    [ ONLINE ]              -- 可选,允许在线转换
    [ UPDATE INDEXES (      -- 可选,用于精细控制索引转换
        index_name { LOCAL | GLOBAL [ partition_definition ] } [, ...]
    ) ];
语法组成部分详解

PARTITION BY 子句(必需)

定义表的分区策略,与 CREATE TABLE 语法类似。

分区类型 语法示例 说明
范围分区 (RANGE) PARTITION BY RANGE (id) (PARTITION p1 VALUES LESS THAN (100), ...) 最常用,适用于按日期、ID等有序字段分区。
间隔分区 (INTERVAL) PARTITION BY RANGE (created_date) INTERVAL (NUMTODSINTERVAL(1,'DAY')) (PARTITION p_init VALUES LESS THAN (DATE '2023-01-01')) RANGE分区的扩展,可自动创建新分区。
列表分区 (LIST) PARTITION BY LIST (region) (PARTITION p1 VALUES ('EAST','WEST'), ...) 适用于枚举值字段。
哈希分区 (HASH) PARTITION BY HASH (id) PARTITIONS 4 适用于数据均匀分布的场景。

SUBPARTITION BY 子句(可选)

用于创建复合分区 表,对每个主分区再分子分区,常见组合有 RANGE-HASHRANGE-LIST 等。

复制代码
ALTER TABLE sales MODIFY
    PARTITION BY RANGE (sale_date) (
        PARTITION p1 VALUES LESS THAN (DATE '2023-01-01'),
        PARTITION p2 VALUES LESS THAN (DATE '2023-02-01')
    )
    SUBPARTITION BY HASH (customer_id) SUBPARTITIONS 8; -- 每个主分区再分为8个哈希子分区

ONLINE 关键字(强烈推荐)

ONLINE 关键字允许在不阻塞DML操作的情况下在线转换表。其内部机制复杂,会创建日志表记录变更并通过批量迁移完成转换。

复制代码
ALTER TABLE your_table MODIFY PARTITION BY RANGE (id) (...) ONLINE;

UPDATE INDEXES 子句(关键)

此子句用于精细控制 表上现有索引的转换方式。如果不指定,Oracle会按默认规则转换,一定要慎重,尽量避免这样,默认不可控

如果确实有索引需要转换,强烈建议明确指定索引的转换方式!!!

复制代码
UPDATE INDEXES (
    idx_name1 LOCAL,                 -- 转为本地分区索引
    idx_name2 GLOBAL,                -- 转为非分区全局索引
    idx_name3 GLOBAL PARTITION BY RANGE (col) (PARTITION p_idx VALUES LESS THAN (MAXVALUE))
)

简单示例

复制代码
1)修改为普通分区类型,类似如下
ALTER TABLE employees_convert MODIFY
  PARTITION BY RANGE (employee_id) INTERVAL (100)
  ( PARTITION P1 VALUES LESS THAN (100),
    PARTITION P2 VALUES LESS THAN (500)
   ) ONLINE   -online 表示在线,不指定为offline,锁表
  UPDATE INDEXES
 ( IDX1_SALARY LOCAL,
   IDX2_EMP_ID GLOBAL PARTITION BY RANGE (employee_id)
  ( PARTITION IP1 VALUES LESS THAN (MAXVALUE))
 );
 
 
2)修改为subpartition分区,类似如下
alter table TEST_TAB modify
partition by range (created_date) subpartition by hash (id)(
partition TEST_TAB_2021 values less than (to_date('01-jan-2022','dd-mon-yyyy')) (
subpartition TEST_TAB_SUB_PART_2021_1,
subpartition TEST_TAB_SUB_PART_2021_2,
subpartition TEST_TAB_SUB_PART_2021_3,
subpartition TEST_TAB_SUB_PART_2021_4
),
partition TEST_TAB_2022 values less than (to_date('01-jan-2023','dd-mon-yyyy')) (
subpartition TEST_TAB_SUB_PART_2022_1,
subpartition TEST_TAB_SUB_PART_2022_2,
subpartition TEST_TAB_SUB_PART_2022_3,
subpartition TEST_TAB_SUB_PART_2022_4
),
partition TEST_TAB_2023 values less than (to_date('01-jan-2024','dd-mon-yyyy')) (
subpartition TEST_TAB_SUB_PART_2023_1,
subpartition TEST_TAB_SUB_PART_2023_2,
subpartition TEST_TAB_SUB_PART_2023_3,
subpartition TEST_TAB_SUB_PART_2023_4
) )
online
update indexes
(
test_tab_pk global,
test_tab_created_date_idx local
);

五、测试案例

1. 实验环境准备

1.1 创建测试表并加载大量数据

复制代码
-- 创建测试表 t,包含 500 万行数据
CREATE TABLE t (
    id          NUMBER,
    col1        NUMBER,
    col2        NUMBER,
    col3        NUMBER,
    col4        NUMBER,
    padding     VARCHAR2(100)
);

-- 插入 200 万行
INSERT /*+ APPEND */ INTO t
SELECT LEVEL,
       MOD(LEVEL, 1000),
       MOD(LEVEL, 500),
       ROUND(DBMS_RANDOM.VALUE(1, 10000)),
       ROUND(DBMS_RANDOM.VALUE(1, 10000)),
       LPAD('X', 100, 'X')
FROM   DUAL
CONNECT BY LEVEL <= 2000000;

COMMIT;

-- 收集统计信息
EXEC DBMS_STATS.GATHER_TABLE_STATS(USER, 'T');

1.2 创建多种类型的索引

复制代码
-- 1. 前缀索引(索引列包含分区键 id)
CREATE INDEX idx_prefix ON t(id);

-- 2. 非前缀普通索引(索引列不包含分区键)
CREATE INDEX idx_nonprefix ON t(col1, col2);

-- 3. 普通索引
CREATE  INDEX idx_col3 ON t(col3);

-- 4. 唯一索引(包含分区键,用于观察全局唯一性约束)
CREATE UNIQUE INDEX idx_unique ON t(id, col4);

1.3 检查索引初始状态

复制代码
set linesize 200 pagesize 999
col INDEX_NAME format a15
col INDEX_TYPE format a15
col UNIQUENESS format a15
SELECT INDEX_NAME, INDEX_TYPE, UNIQUENESS, PARTITIONED
FROM USER_INDEXES
WHERE TABLE_NAME = 'T';

INDEX_NAME      INDEX_TYPE      UNIQUENESS      PARTITION
--------------- --------------- --------------- ---------
IDX_UNIQUE      NORMAL          UNIQUE          NO
IDX_COL3        NORMAL          NONUNIQUE       NO
IDX_NONPREFIX   NORMAL          NONUNIQUE       NO
IDX_PREFIX      NORMAL          NONUNIQUE       NO
2. 场景一:默认行为(省略 UPDATE INDEXES

2.1 执行分区转换

复制代码
-- 不指定 UPDATE INDEXES,由 Oracle 自动决定索引转换方式
set timing on
ALTER TABLE t MODIFY
    PARTITION BY RANGE (id) (
        PARTITION p1 VALUES LESS THAN (1000000),
        PARTITION p2 VALUES LESS THAN (2000000),
        PARTITION p3 VALUES LESS THAN (3000000),
        PARTITION p4 VALUES LESS THAN (4000000),
        PARTITION p5 VALUES LESS THAN (MAXVALUE)
    );

2.2 检查转换后的索引状态

复制代码
-- 查看索引是否分区以及类型
SELECT INDEX_NAME, PARTITIONED, STATUS
FROM USER_INDEXES
WHERE TABLE_NAME = 'T';

INDEX_NAME      PAR STATUS
--------------- --- --------
IDX_PREFIX      YES N/A
IDX_NONPREFIX   NO  VALID
IDX_COL3        NO  VALID
IDX_UNIQUE      YES N/A

-- 查看分区索引的各分区状态
col partition_name format a30
SELECT INDEX_NAME, PARTITION_NAME, STATUS
FROM USER_IND_PARTITIONS
WHERE INDEX_NAME IN ('IDX_PREFIX', 'IDX_UNIQUE')
ORDER BY INDEX_NAME, PARTITION_NAME;

INDEX_NAME      PARTITION_NAME                 STATUS
--------------- ------------------------------ --------
IDX_PREFIX      P1                             USABLE
IDX_PREFIX      P2                             USABLE
IDX_PREFIX      P3                             USABLE
IDX_PREFIX      P4                             USABLE
IDX_PREFIX      P5                             USABLE
IDX_UNIQUE      P1                             USABLE
IDX_UNIQUE      P2                             USABLE
IDX_UNIQUE      P3                             USABLE
IDX_UNIQUE      P4                             USABLE
IDX_UNIQUE      P5                             USABLE

2.3 结果

不指定(省略 UPDATE INDEXES),Oracle会按默认规则转换表上的索引!

索引名 分区状态 说明
IDX_PREFIX YES 因为包含分区键 id,自动转换为本地分区索引
IDX_NONPREFIX NO 不包含分区键,自动保留为非分区全局索引
IDX_COL3 NO 不包含分区键,自动保留为非分区全局索引
IDX_UNIQUE YES 唯一索引包含分区键,自动转换为本地分区索引(但若包含唯一约束,需注意是否有跨分区唯一性)

3. 场景二:各种选项下的redo日志产生量

主要考虑online和非online模式,以及是否更新索引,这几种情况下的redo日志产生量

0、准备工作,重建初始表(与场景一完全一致)

为避免干扰,重新创建相同的表结构和数据,或者将表改回普通表并重新创建索引。

复制代码
-- 回退方法(若允许)
DROP TABLE t PURGE;
-- 重新执行 1.1 和 1.2 步骤重建
-- 索引是否创建,根据场景来决定

1、有索引,非online模式,redo日志产生量

复制代码
-- 不指定 UPDATE INDEXES,由 Oracle 自动决定索引转换方式
set timing on
ALTER TABLE t MODIFY
    PARTITION BY RANGE (id) (
        PARTITION p1 VALUES LESS THAN (1000000),
        PARTITION p2 VALUES LESS THAN (2000000),
        PARTITION p3 VALUES LESS THAN (3000000),
        PARTITION p4 VALUES LESS THAN (4000000),
        PARTITION p5 VALUES LESS THAN (MAXVALUE)
    );
    
col name format a20
select a.name,b.value 
from v$statname a,v$mystat  b
where a.statistic# = b.statistic# and a.name='redo size';


非online模式下,有索引
NAME                      VALUE
-------------------- ----------
redo size               1908180

2、无索引,非online模式,redo日志产生量

复制代码
set timing on
ALTER TABLE t MODIFY
    PARTITION BY RANGE (id) (
        PARTITION p1 VALUES LESS THAN (1000000),
        PARTITION p2 VALUES LESS THAN (2000000),
        PARTITION p3 VALUES LESS THAN (3000000),
        PARTITION p4 VALUES LESS THAN (4000000),
        PARTITION p5 VALUES LESS THAN (MAXVALUE)
    );
    
col name format a20
select a.name,b.value 
from v$statname a,v$mystat  b
where a.statistic# = b.statistic# and a.name='redo size';

非online模式下,无索引
NAME                      VALUE
-------------------- ----------
redo size                683044

3、无索引,online模式,redo日志产生量

复制代码
set linesize 200 pagesize 999
col INDEX_NAME format a15
col INDEX_TYPE format a15
col UNIQUENESS format a15
col name format a20
select a.name,b.value 
from v$statname a,v$mystat  b
where a.statistic# = b.statistic# and a.name='redo size';

ALTER TABLE t MODIFY
    PARTITION BY RANGE (id) (
        PARTITION p1 VALUES LESS THAN (1000000),
        PARTITION p2 VALUES LESS THAN (2000000),
        PARTITION p3 VALUES LESS THAN (3000000),
        PARTITION p4 VALUES LESS THAN (4000000),
        PARTITION p5 VALUES LESS THAN (MAXVALUE)
    ) online;
select a.name,b.value 
from v$statname a,v$mystat  b
where a.statistic# = b.statistic# and a.name='redo size';    
    

online模式下,无索引
NAME                      VALUE
-------------------- ----------
redo size               1327032

4、有索引,online模式,redo日志产生量

复制代码
set linesize 200 pagesize 999
col INDEX_NAME format a15
col INDEX_TYPE format a15
col UNIQUENESS format a15
col name format a20
select a.name,b.value 
from v$statname a,v$mystat  b
where a.statistic# = b.statistic# and a.name='redo size';

ALTER TABLE t MODIFY
    PARTITION BY RANGE (id) (
        PARTITION p1 VALUES LESS THAN (1000000),
        PARTITION p2 VALUES LESS THAN (2000000),
        PARTITION p3 VALUES LESS THAN (3000000),
        PARTITION p4 VALUES LESS THAN (4000000),
        PARTITION p5 VALUES LESS THAN (MAXVALUE)
    ) online
    UPDATE INDEXES (
        idx_prefix LOCAL,
        idx_nonprefix GLOBAL,
        IDX_COL3 LOCAL,
        idx_unique GLOBAL
    );
    
select a.name,b.value 
from v$statname a,v$mystat  b
where a.statistic# = b.statistic# and a.name='redo size';

online模式下,有索引
NAME                      VALUE
-------------------- ----------
redo size               2528856

结果对比:

modify改造模式 redo日志量 执行速度
非online模式下,有索引 1908180 第三
非online模式下,无索引 683044 第一
online模式下,无索引 1327032 第二
online模式下,有索引 2528856 第四

结论 :如果条件允许,建议提前删除非必要索引,转换后再重建,以减少REDO并加快整体速度

复制代码
相关推荐
Gent_倪1 小时前
MySQL 与 Hive 语法异同点详解
数据库·hive·mysql
七夜zippoe1 小时前
DolphinDB 故障排查实战:系统、数据库与集群常见问题诊断
数据库·wpf·集群·常见问题·故障排查·dolphindb
!chen2 小时前
数据库主从有延迟怎么处理
数据库·adb
SelectDB技术团队2 小时前
Apache Doris 多云原生实践:SaaS、BYOC 与四大公有云覆盖
数据库·阿里云·云原生·华为云·腾讯云·亚马逊云·写入更新
云飞云共享云桌面4 小时前
医疗器械设备研发:多人同时操作 SolidWorks,怎样依靠单台服务器替代多工作站
运维·服务器·网络·数据库·制造
知行合一。。。9 小时前
RAG--03--Milvus基本用法
数据库·oracle·milvus
TDengine (老段)10 小时前
TDengine 线程模型 — 网络、调度、执行
大数据·数据库·物联网·制造·时序数据库·tdengine·涛思数据
上学的小垃圾11 小时前
01-数据库系统概述
数据库
二进制漫游记11 小时前
FastAPI项目集成 Qdrant向量数据库+阿里云Embedding完整实战(工具封装+业务调用)
数据库·python·阿里云·embedding