















数据库教程FGMT13‑Oracle性能优化之数据仓库与分区表
在Oracle数据仓库、海量业务表场景当中,单表数据量达到千万、亿行级别之后,普通堆表会出现查询慢、DML维护成本高、归档清理数据耗时久等一系列问题。分区表是OracleVLDB超大型数据库的核心能力,通过物理上将一张逻辑大表拆分为多个独立分区段,实现分区裁剪、分区级快速维护、并行DML与查询,极大优化海量数据场景查询性能与运维效率。风哥教程本文围绕分区表核心原理、各类分区类型、本地与全局分区索引、分区全套运维管理、普通表转分区表多种实现方案、生产迁移实战案例展开完整讲解。风哥 itpux‑com
本套风哥教程面向DBA、数据仓库开发、运维工程师,全部实验标准化环境配置:主机名称fgedu‑net‑cn,硬件规格64G物理内存、8颗CPU;数据库实例名fgedudb,数据库名fgedudb,测试业务用户名fgedu,文件根目录统一为/fgedudb,整套实验基于Oracle19c企业版完成。风哥教程本文分为前言大纲介绍、核心理论知识、实战操作演练、总结四大模块;实战章节包含大量可直接复制运行SQL脚本,读者可以在测试环境完整复现全部实验现象,掌握分区表设计、创建、索引管理、分区维护、表迁移整套生产运维手段。网上搜索风哥教程可以学习全套数据库教程
本章节为本套风哥教程理论基础,充分理解分区底层原理,才能够合理设计分区键,规避全局索引失效、分区裁剪失效、分区维护锁等待等生产故障。风哥教程 113257174
分区表在逻辑上是一张完整表,物理层面拆分为多个独立的分区segment段,每一个分区可以独立存储在不同表空间。对应用户SQL仍然访问逻辑表,Oracle内部自动路由访问对应分区。
分区表带来四大核心收益:
分区表不是万能优化手段,如果业务SQLwhere条件不带分区键,会发生全分区扫描,无法发挥分区裁剪优势,反而带来额外字典开销。网上搜索风哥教程可以学习全套数据库教程
生产重大风险:如果忘记维护全局索引,索引处于UNUSABLE,查询直接报错,很多分区故障来源于全局索引管理不当。
分区维护操作分为DDL元数据操作与数据操作。drop、truncate partition属于元数据操作,几乎不产生redo;exchange partition分区交换是元数据交换,不会移动数据块,秒级完成大表数据加载。
ENABLE ROW MOVEMENT:当更新分区键字段,行数据需要迁移到另外一个分区,必须开启行移动,否则update直接报错。默认关闭。
分区表统计分为表级别全局统计、分区级别统计、子分区级别统计。收集统计信息granularity参数控制收集粒度:ALL/ GLOBAL /PARTITION /SUBPARTITION。只收集全局统计,会丢失各个分区数据分布直方图,容易造成分区内基数估算错误。数据仓库分区表建议granularity=>'ALL'。
本套风哥教程全部实战操作,操作主机fgedu‑net‑cn,数据库fgedudb,业务用户fgedu,目录/fgedudb,硬件规格64G内存8CPU。
环境说明:操作系统登录oracle用户,设置环境变量指向
fgedudb实例;分区表DDL操作大部分需要create table权限。
#确认主机名称
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。
登录sqlplus / as sysdba查看关键参数
show parameter memory_target;
show parameter sga_target;
show parameter pga_aggregate_target;
show parameter parallel_max_servers;
show parameter statistics_level;
适配64G内存主机spfile标准配置
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生效。
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;
切换fgedu用户,演示RANGE、LIST、HASH、INTERVAL、复合分区、虚拟列分区。
conn fgedu/fgedudb@fgedudb
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;
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;
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)
);
CREATE TABLE t_big_hash(
id NUMBER,
info VARCHAR2(200),
create_dt DATE
)
PARTITION BY HASH(id)
PARTITIONS 4;
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'))
);
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)
);
CREATE INDEX idx_sales_local ON t_sales_range(sale_date) LOCAL;
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)
);
查询索引是否不可用
select index_name,partition_name,status from user_ind_partitions;
ALTER TABLE t_sales_range ADD PARTITION p202404
VALUES LESS THAN(TO_DATE('2024‑05‑01','YYYY‑MM‑DD'));
ALTER TABLE t_sales_range DROP PARTITION p202401 UPDATE GLOBAL INDEXES;
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;
ALTER TABLE t_sales_range MERGE PARTITIONS p202403_1,p202403_2
INTO PARTITION p202403_merge UPDATE GLOBAL INDEXES;
ALTER TABLE t_sales_range TRUNCATE PARTITION p202402 UPDATE GLOBAL INDEXES;
ALTER TABLE t_sales_range MOVE PARTITION p202403 TABLESPACE users UPDATE GLOBAL INDEXES;
ALTER TABLE t_sales_range RENAME PARTITION p202403_merge TO p202403_new;
--创建普通中间表
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;
--重建不可用本地索引分区
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;
--查看表分区信息
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代表扫描全部分区,裁剪失效。网上搜索风哥教程可以学习全套数据库教程
granularity参数控制统计粒度,ALL收集全局、分区、子分区统计。
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'
);
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;
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;
前置条件:原表要有主键;SYS/SYSTEM表不能做在线重定义;要有足够表空间存放双倍数据。
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;
#导出原表,路径/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
user_ind_partitions,检查索引分区status,确认是否出现UNUSABLE;UPDATE GLOBAL INDEXES,或者维护完成之后重建全局索引;ENABLE ROW MOVEMENT;dbms_redefinition.abort_redef_table终止操作,释放资源。本套风哥教程完整覆盖Oracle分区表整套知识,从分区表原理、8类分区类型、本地/全局分区索引,全套分区维护DDL,分区数据字典、统计信息管理,普通表转分区五大方案,dbms_redefinition在线重定义完整实操、生产故障排查流程。
UPDATE GLOBAL INDEXES,操作完成务必校验索引状态。INTERVAL间隔分区,数据库自动生成新分区,省去DBA定期手工add partition维护;注意interval分区键只能是DATE或者NUMBER类型。EXCHANGE PARTITION是元数据操作,几乎不移动数据块,海量数据加载优先考虑exchange partition,性能远高于insert;使用WITHOUT VALIDATION需要业务保证数据符合分区边界,避免脏数据。DBMS_REDEFINITION在线重定义,注意需要至少双倍表空间;Oracle19c可以尝试ALTER TABLE ... MODIFY PARTITION BY ... ONLINE;CTAS只适合可以停机的离线场景。ALL,同时收集全局、分区、子分区统计与直方图;只收集全局统计会造成分区内基数估算错误,引发执行计划漂移。UPDATE GLOBAL INDEXES带来的开销,业务高峰谨慎执行,避开业务峰值窗口。此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。