前言
在Oracle数据仓库、海量业务表场景当中,单表数据量达到千万、亿行级别之后,普通堆表会出现查询慢、DML维护成本高、归档清理数据耗时久等一系列问题。分区表是OracleVLDB超大型数据库的核心能力,通过物理上将一张逻辑大表拆分为多个独立分区段,实现分区裁剪、分区级快速维护、并行DML与查询,极大优化海量数据场景查询性能与运维效率。风哥教程本文围绕分区表核心原理、各类分区类型、本地与全局分区索引、分区全套运维管理、普通表转分区表多种实现方案、生产迁移实战案例展开完整讲解。风哥 itpux‑com
本套风哥教程面向DBA、数据仓库开发、运维工程师,全部实验标准化环境配置:主机名称fgedu‑net‑cn ,硬件规格64G物理内存、8颗CPU;数据库实例名fgedudb,数据库名fgedudb,测试业务用户名fgedu,文件根目录统一为/fgedudb,整套实验基于Oracle19c企业版完成。风哥教程本文分为前言大纲介绍、核心理论知识、实战操作演练、总结四大模块;实战章节包含大量可直接复制运行SQL脚本,读者可以在测试环境完整复现全部实验现象,掌握分区表设计、创建、索引管理、分区维护、表迁移整套生产运维手段。网上搜索风哥教程可以学习全套数据库教程
内容大纲
- Oracle数据仓库与分区表基础概念,分区表核心价值与适用场景
- Oracle各类分区类型原理:范围、列表、哈希、复合、Interval间隔、虚拟列、系统分区、参考分区
- 本地分区索引、全局分区索引原理,二者差异、适用场景、失效风险
- 分区表各类管理操作:增加、删除、拆分、收缩、合并、修改、截断、移动、重命名
- 分区索引管理:增加、删除、编译、重命名、拆分、重建索引分区
- 分区相关数据字典视图,分区表与分区索引统计信息收集管理
- 普通表转为分区表五大实现方案,各自优缺点与适用场景
- 分区表迁移生产实战案例,dbms_redefinition在线重定义完整实操
- 分区裁剪、分区级并行访问原理,数据仓库场景最佳实践
- 分区表生产风险点、设计规范、故障排查工作流程
一、核心理论知识
本章节为本套风哥教程理论基础,充分理解分区底层原理,才能够合理设计分区键,规避全局索引失效、分区裁剪失效、分区维护锁等待等生产故障。风哥教程 113257174
1.1 分区表基础概念与核心价值
分区表在逻辑上是一张完整表,物理层面拆分为多个独立的分区segment段,每一个分区可以独立存储在不同表空间。对应用户SQL仍然访问逻辑表,Oracle内部自动路由访问对应分区。
分区表带来四大核心收益:
- 分区裁剪(Partition Pruning):SQL where条件携带分区键,优化器自动过滤不需要访问的分区,只扫描少量分区,大幅降低IO开销,提升查询性能。
- 分区级运维:可以直接对分区执行drop、truncate、exchange,海量历史数据归档清理不再需要delete全表,元数据操作秒级完成,不会产生大量undo/redo。
- 并行处理:查询、DML、备份恢复可以以分区为粒度做并行,适配64G内存8CPU多核算力。
- 存储隔离:不同分区可以放置不同表空间,冷热数据分开存储,历史冷数据可以放到低成本存储介质。
分区表不是万能优化手段,如果业务SQLwhere条件不带分区键,会发生全分区扫描,无法发挥分区裁剪优势,反而带来额外字典开销。网上搜索风哥教程可以学习全套数据库教程
1.2 Oracle19c各类分区类型原理
- 范围分区RANGE:最常用,依据字段数值、日期范围划分分区,适合时序业务,例如按月份、按时间归档交易流水。
- 列表分区LIST:依据字段离散枚举值分区,例如业务状态、地区编码,每一个分区对应一组固定枚举值。
- 哈希分区HASH:依据哈希算法打散数据,数据均匀分布各个分区,没有明确范围边界,适合无明显时间维度,需要均匀打散大表IO压力的场景。
- 复合分区COMPOSITE:两级分区,一级分区基础之上再设置子分区,例如RANGE‑LIST、RANGE‑HASH、LIST‑HASH,数据仓库事实表大量使用复合分区。
- Interval间隔分区 :RANGE分区的扩展,当插入数据超出已有分区边界,数据库自动生成新分区,不需要DBA手工定期add partition,时序业务首选。Interval分区键只能是DATE或者NUMBER类型。
- 虚拟列分区:基于表达式计算虚拟列做分区键,业务表不需要增加真实物理字段,直接使用字段运算结果做分区。
- 系统分区SYSTEM:不由数据库自动路由数据,DBA必须显示指定partition子句插入数据,数据完全由应用控制,使用场景极少。
- 参考分区REFERENCE:子表跟随父表外键做分区,子表不需要重复存储分区键,父子表分区一一对应,主从业务表使用。风哥数据库教程 itpux‑com
1.3 本地分区索引LOCAL与全局分区索引GLOBAL
- 本地分区索引LOCAL:索引分区与表分区一一对应,一个表分区对应一个索引分区。表分区做drop、truncate操作,只会影响对应索引分区,其余索引分区保持可用,不容易出现索引全部失效。OLTP、数据仓库环境优先选用本地索引。
- 全局索引GLOBAL :全局索引独立于表分区,拥有自己的分区规则,和表分区不需要一一对应。分区执行drop、truncate、split等维护操作之后,全局索引会变为UNUSABLE不可用状态,需要重建索引;可以使用UPDATE GLOBAL INDEXES子句,维护操作同步更新全局索引,但会带来额外性能开销。
生产重大风险:如果忘记维护全局索引,索引处于UNUSABLE,查询直接报错,很多分区故障来源于全局索引管理不当。
1.4 分区维护操作基础理论
分区维护操作分为DDL元数据操作与数据操作。drop、truncate partition属于元数据操作,几乎不产生redo;exchange partition分区交换是元数据交换,不会移动数据块,秒级完成大表数据加载。
ENABLE ROW MOVEMENT:当更新分区键字段,行数据需要迁移到另外一个分区,必须开启行移动,否则update直接报错。默认关闭。
1.5 普通表转为分区表五大方案对比
- 方案一:CTAS创建分区表+insert插入数据:简单,会锁原表,业务必须停机,适合离线测试库。
- 方案二:交换分区exchange partition:新建空分区表,创建中间普通表,交换分区;适合可以分批次迁移历史数据场景。
- 方案三:dbms_redefinition在线重定义:业务不停机在线转换,支持24*7业务,需要足够双倍表空间,生产最常用。
- 方案四:19c ALTER TABLE ... MODIFY PARTITION BY ... ONLINE:19c新特性,online直接修改表定义转为分区表,语法简洁,但版本有要求。
- 方案五:expdp/impdp数据泵导出导入:适合跨库迁移,停机窗口。
1.6 分区统计信息管理
分区表统计分为表级别全局统计、分区级别统计、子分区级别统计。收集统计信息granularity参数控制收集粒度:ALL/ GLOBAL /PARTITION /SUBPARTITION。只收集全局统计,会丢失各个分区数据分布直方图,容易造成分区内基数估算错误。数据仓库分区表建议granularity=>'ALL'。
二、实战操作演练
本套风哥教程全部实战操作,操作主机fgedu‑net‑cn,数据库fgedudb,业务用户fgedu,目录/fgedudb,硬件规格64G内存8CPU。
环境说明:操作系统登录oracle用户,设置环境变量指向
fgedudb实例;分区表DDL操作大部分需要create table权限。
2.1 操作系统与数据库环境校验
2.1.1操作系统层面检查(主机fgedu‑net‑cn)
bash
#确认主机名称
hostname
#确认ORACLE_HOME路径全部指向/fgedudb
echo $ORACLE_HOME
#查看内存硬件,确认64G内存
free -h
#查看CPU,确认8CPU
lscpu
#确认表空间数据文件目录
ls -ld /fgedudb/oradata/fgedudb
校验输出:hostname输出fgedu‑net‑cn,总内存64G,逻辑CPU数量8。
2.1.2 数据库关键初始化参数确认(64G内存8CPU规格)
登录sqlplus / as sysdba查看关键参数
sql
show parameter memory_target;
show parameter sga_target;
show parameter pga_aggregate_target;
show parameter parallel_max_servers;
show parameter statistics_level;
适配64G内存主机spfile标准配置
sql
alter system set memory_max_target=48G scope=spfile;
alter system set memory_target=48G scope=spfile;
alter system set parallel_max_servers=32 scope=spfile;
alter system set statistics_level=TYPICAL scope=spfile;
parallel_max_servers适配8CPU,分区并行查询、并行DML依赖该参数。重启实例spfile生效。
2.1.3 用户权限准备
sql
create user fgedu identified by fgedudb default tablespace users temporary tablespace temp;
grant connect,resource to fgedu;
grant select_catalog_role to fgedu;
grant create any table,alter any table,drop any table to fgedu;
grant execute on dbms_redefinition to fgedu;
grant execute on dbms_stats to fgedu;
2.2 各类分区表创建实战
切换fgedu用户,演示RANGE、LIST、HASH、INTERVAL、复合分区、虚拟列分区。
sql
conn fgedu/fgedudb@fgedudb
2.2.1 RANGE范围分区(按交易时间)
sql
CREATE TABLE t_sales_range (
sale_id NUMBER,
sale_date DATE,
cust_id NUMBER,
amount NUMBER(12,2)
)
PARTITION BY RANGE (sale_date)
(
PARTITION p202401 VALUES LESS THAN (TO_DATE('2024‑02‑01','YYYY‑MM‑DD')),
PARTITION p202402 VALUES LESS THAN (TO_DATE('2024‑03‑01','YYYY‑MM‑DD')),
PARTITION p202403 VALUES LESS THAN (TO_DATE('2024‑04‑01','YYYY‑MM‑DD'))
)
ENABLE ROW MOVEMENT;
2.2.2 INTERVAL间隔自动分区(时序业务,自动生成新分区)
sql
CREATE TABLE t_sales_interval (
sale_id NUMBER,
sale_date DATE,
cust_id NUMBER,
amount NUMBER(12,2)
)
PARTITION BY RANGE (sale_date)
INTERVAL(NUMTOYMINTERVAL(1,'MONTH'))
(
PARTITION p_init VALUES LESS THAN (TO_DATE('2024‑01‑01','YYYY‑MM‑DD'))
)
ENABLE ROW MOVEMENT;
--插入超过初始边界日期,数据库自动新建分区
INSERT INTO t_sales_interval values(1,SYSDATE,100,2000);
commit;
2.2.3 LIST列表分区,按业务状态
sql
CREATE TABLE t_order_list (
order_id NUMBER,
order_status VARCHAR2(20),
cust_id NUMBER,
create_time DATE
)
PARTITION BY LIST (order_status)
(
PARTITION p_new VALUES ('NEW'),
PARTITION p_pay VALUES ('PAY'),
PARTITION p_finish VALUES ('FINISH'),
PARTITION p_other VALUES (DEFAULT)
);
2.2.4 HASH哈希分区,打散IO压力
sql
CREATE TABLE t_big_hash(
id NUMBER,
info VARCHAR2(200),
create_dt DATE
)
PARTITION BY HASH(id)
PARTITIONS 4;
2.2.5 复合分区 RANGE‑LIST 一级时间,二级状态
sql
CREATE TABLE t_comp_rl (
sale_id NUMBER,
sale_date DATE,
order_status VARCHAR2(20),
amount NUMBER(12,2)
)
PARTITION BY RANGE(sale_date)
SUBPARTITION BY LIST(order_status)
SUBPARTITION TEMPLATE(
SUBPARTITION sp_new VALUES('NEW'),
SUBPARTITION sp_pay VALUES('PAY'),
SUBPARTITION sp_other VALUES(DEFAULT)
)
(
PARTITION p202401 VALUES LESS THAN(TO_DATE('2024‑02‑01','YYYY‑MM‑DD')),
PARTITION p202402 VALUES LESS THAN(TO_DATE('2024‑03‑01','YYYY‑MM‑DD'))
);
2.2.6 虚拟列分区
sql
CREATE TABLE t_virtual_part(
id NUMBER,
province_code VARCHAR2(10),
amount NUMBER
)
PARTITION BY RANGE (MOD(id,10))
(
PARTITION p0 VALUES LESS THAN(1),
PARTITION p1 VALUES LESS THAN(2),
PARTITION p_max VALUES LESS THAN(MAXVALUE)
);
2.3 本地LOCAL索引、全局GLOBAL分区索引创建实操
2.3.1 创建本地分区索引
sql
CREATE INDEX idx_sales_local ON t_sales_range(sale_date) LOCAL;
2.3.2 创建全局分区索引
sql
CREATE INDEX idx_sales_global ON t_sales_range(sale_id)
GLOBAL PARTITION BY RANGE(sale_id)
(
PARTITION g_p1 VALUES LESS THAN(100000),
PARTITION g_p2 VALUES LESS THAN(200000),
PARTITION g_pmax VALUES LESS THAN(MAXVALUE)
);
查询索引是否不可用
sql
select index_name,partition_name,status from user_ind_partitions;
2.4 分区表日常维护DDL完整实操
2.4.1 新增分区(interval分区不需要add partition)
sql
ALTER TABLE t_sales_range ADD PARTITION p202404
VALUES LESS THAN(TO_DATE('2024‑05‑01','YYYY‑MM‑DD'));
2.4.2 删除分区,UPDATE GLOBAL INDEXES避免全局索引失效
sql
ALTER TABLE t_sales_range DROP PARTITION p202401 UPDATE GLOBAL INDEXES;
2.4.3 SPLIT拆分分区,拆分maxvalue分区
sql
ALTER TABLE t_sales_range SPLIT PARTITION p202403
AT(TO_DATE('2024‑03‑15','YYYY‑MM‑DD'))
INTO(PARTITION p202403_1,PARTITION p202403_2)
UPDATE GLOBAL INDEXES;
2.4.4 MERGE合并两个相邻分区
sql
ALTER TABLE t_sales_range MERGE PARTITIONS p202403_1,p202403_2
INTO PARTITION p202403_merge UPDATE GLOBAL INDEXES;
2.4.5 TRUNCATE截断分区(快速清空分区数据)
sql
ALTER TABLE t_sales_range TRUNCATE PARTITION p202402 UPDATE GLOBAL INDEXES;
2.4.6 MOVE移动分区到其他表空间
sql
ALTER TABLE t_sales_range MOVE PARTITION p202403 TABLESPACE users UPDATE GLOBAL INDEXES;
2.4.7 RENAME重命名分区
sql
ALTER TABLE t_sales_range RENAME PARTITION p202403_merge TO p202403_new;
2.4.8 EXCHANGE PARTITION分区交换,秒级加载数据
sql
--创建普通中间表
create table t_stg as select * from t_sales_range where 1=0;
--向中间表插入数据
insert into t_stg select * from t_sales_range where sale_date>=TO_DATE('2024‑03‑01','YYYY‑MM‑DD');
commit;
--交换分区,元数据操作,几乎不IO拷贝数据
ALTER TABLE t_sales_range EXCHANGE PARTITION p202403_new WITH TABLE t_stg WITHOUT VALIDATION;
2.5 分区索引维护操作实操
sql
--重建不可用本地索引分区
ALTER INDEX idx_sales_local REBUILD PARTITION p202403;
--重建全局索引
ALTER INDEX idx_sales_global REBUILD;
--重命名索引分区
ALTER INDEX idx_sales_local RENAME PARTITION p202403 TO idx_p202403;
2.6 分区相关数据字典查询
sql
--查看表分区信息
select table_name,partition_name,high_value,tablespace_name from user_tab_partitions;
--查看子分区(复合分区)
select table_name,partition_name,subpartition_name from user_tab_subpartitions;
--查看索引分区状态
select index_name,partition_name,status from user_ind_partitions;
--查看分区裁剪,explain plan看Pstart Pstop
explain plan for select * from t_sales_range where sale_date=TO_DATE('2024‑02‑05','YYYY‑MM‑DD');
select * from table(dbms_xplan.display());
执行计划中
Pstart/Pstop不是MAXVALUE代表发生分区裁剪;Pstart:Pstop为MAXVALUE代表扫描全部分区,裁剪失效。网上搜索风哥教程可以学习全套数据库教程
2.7 分区表统计信息收集实操
granularity参数控制统计粒度,ALL收集全局、分区、子分区统计。
sql
exec dbms_stats.gather_table_stats(
ownname=>'FGEDU',
tabname=>'T_SALES_RANGE',
estimate_percent=>dbms_stats.auto_sample_size,
method_opt=>'FOR ALL COLUMNS SIZE AUTO',
degree=>4,
cascade=>true,
granularity=>'ALL'
);
2.8 普通表转分区表五大方案演示
方案1:CTAS(停机,业务不可用)
sql
create table t_part_ctas partition by range(id)
(partition p1 values less than (1000),partition p2 values less than(maxvalue))
as select * from t_big_hash;
方案2:19c ONLINE直接修改表定义转为分区表
sql
alter table t_big_hash modify
partition by range(id)
(partition p_1 values less than(1000),
partition p_max values less than(maxvalue))
ONLINE UPDATE INDEXES;
方案3:DBMS_REDEFINITION在线重定义(生产不停机重点案例)风哥数据库教程 itpux‑com
前置条件:原表要有主键;SYS/SYSTEM表不能做在线重定义;要有足够表空间存放双倍数据。
sql
conn fgedu/fgedudb@fgedudb
--1.准备源普通表
create table t_origin_big(id number primary key,sale_date date,amt number);
insert into t_origin_big select rownum,sysdate‑mod(rownum,365),rownum from dual connect by rownum<=200000;
commit;
--2.创建中间过渡分区表(只建结构,无数据)
CREATE TABLE t_interim_part(
id number primary key,
sale_date date,
amt number
)
PARTITION BY RANGE(sale_date)
INTERVAL(NUMTOYMINTERVAL(1,'MONTH'))
(PARTITION p_start VALUES LESS THAN (TO_DATE('2023‑01‑01','YYYY‑MM‑DD')));
--3.校验是否满足重定义条件
DECLARE
v_ret NUMBER;
BEGIN
v_ret:=dbms_redefinition.can_redef_table(uname=>'FGEDU',tname=>'T_ORIGIN_BIG');
dbms_output.put_line('校验返回:'||v_ret);
END;
/
--4.启动在线重定义,源表、中间过渡表
BEGIN
dbms_redefinition.start_redef_table(
uname=>'FGEDU',
orig_table=>'T_ORIGIN_BIG',
int_table=>'T_INTERIM_PART'
);
END;
/
--可选:同步增量,多次执行同步中间表捕获业务新增DML
BEGIN
dbms_redefinition.sync_interim_table(uname=>'FGEDU',orig_table=>'T_ORIGIN_BIG',int_table=>'T_INTERIM_PART');
END;
/
--5.完成重定义,短暂排他锁切换元数据
BEGIN
dbms_redefinition.finish_redef_table(uname=>'FGEDU',orig_table=>'T_ORIGIN_BIG',int_table=>'T_INTERIM_PART');
END;
/
--完成之后:T_ORIGIN_BIG已经变成分区表,t_interim_part变为原来普通表,可以drop清理
drop table t_interim_part purge;
方案4:Exchange partition交换分区分批迁移
- 新建目标空分区表;
- 建立多张中间普通表,分批导出原表数据;
- 循环执行exchange partition把中间表数据交换进入各个分区。适合超大表分批迁移,减少锁压力。
方案5:数据泵expdp/impdp导出导入,停机迁移
bash
#导出原表,路径/fgedudb/dump
expdp fgedu/fgedudb@fgedudb directory=DATA_PUMP_DIR dumpfile=part_exp.dmp tables=T_ORIGIN_BIG logfile=part_exp.log
#导入目标分区表
impdp fgedu/fgedudb@fgedudb directory=DATA_PUMP_DIR dumpfile=part_exp.dmp tables=T_ORIGIN_BIG REMAP_TABLE=T_ORIGIN_BIG:T_PART_TARGET logfile=part_imp.log
2.9 分区表生产故障排查流程
- SQL性能差,首先查看执行计划Pstart、Pstop,确认分区裁剪是否生效,Pstart=MAXVALUE代表全分区扫描,需要优化where条件带上分区键。
- 分区维护操作之后查询
user_ind_partitions,检查索引分区status,确认是否出现UNUSABLE; - 全局索引操作务必带上
UPDATE GLOBAL INDEXES,或者维护完成之后重建全局索引; - 查看分区表统计,检查granularity粒度,确认分区级统计、直方图是否收集;
- 更新分区键字段报错ORA‑14402,检查是否开启
ENABLE ROW MOVEMENT; - interval分区插入数据不自动生成,检查分区键字段类型只能是DATE/NUMBER;
- 在线重定义失败,调用
dbms_redefinition.abort_redef_table终止操作,释放资源。
三、风哥针对本文总结
本套风哥教程完整覆盖Oracle分区表整套知识,从分区表原理、8类分区类型、本地/全局分区索引,全套分区维护DDL,分区数据字典、统计信息管理,普通表转分区五大方案,dbms_redefinition在线重定义完整实操、生产故障排查流程。
- 分区表核心收益来自分区裁剪,业务SQL查询条件尽量带上分区键;如果大量业务SQL不带分区键,分区表不会带来性能收益,不建议使用分区。
- 优先使用本地LOCAL分区索引 ;全局GLOBAL索引维护成本很高,drop/truncate/split分区极易导致索引UNUSABLE,维护操作必须带上
UPDATE GLOBAL INDEXES,操作完成务必校验索引状态。 - 时序流水业务优先选择
INTERVAL间隔分区,数据库自动生成新分区,省去DBA定期手工add partition维护;注意interval分区键只能是DATE或者NUMBER类型。 - 分区交换
EXCHANGE PARTITION是元数据操作,几乎不移动数据块,海量数据加载优先考虑exchange partition,性能远高于insert;使用WITHOUT VALIDATION需要业务保证数据符合分区边界,避免脏数据。 - 普通大表转分区表:24*7不停机业务优先
DBMS_REDEFINITION在线重定义,注意需要至少双倍表空间;Oracle19c可以尝试ALTER TABLE ... MODIFY PARTITION BY ... ONLINE;CTAS只适合可以停机的离线场景。 - 分区表统计信息收集,granularity建议设置为
ALL,同时收集全局、分区、子分区统计与直方图;只收集全局统计会造成分区内基数估算错误,引发执行计划漂移。 - 生产分区维护DDL(drop、split、merge、truncate、move),只要存在全局索引,必须评估
UPDATE GLOBAL INDEXES带来的开销,业务高峰谨慎执行,避开业务峰值窗口。 - 设计分区的时候,合理规划分区大小,避免产生大量极小分区,分区数量过多会加重数据字典负载,影响解析性能。