Skip to content

Oracle 分区表管理

分区表的优势

分区表为 Oracle 数据库提供以下核心优势:

  • 提高查询性能:只扫描相关分区,减少 I/O
  • 简化数据管理:按分区进行数据加载、删除和归档
  • 提高可用性:单个分区故障不影响整个表
  • 平衡 I/O 分布:数据分布到多个物理设备
  • 支持更大的表:突破单表大小限制
  • 简化备份和恢复:可按分区进行备份

分区类型

范围分区

  • 定义:基于列值的范围进行分区
  • 适用场景:时间序列数据、按范围分布的数据
  • 示例
sql
CREATE TABLE sales (
    sale_id NUMBER,
    sale_date DATE,
    amount NUMBER
)
PARTITION BY RANGE (sale_date) (
    PARTITION p2023_q1 VALUES LESS THAN (TO_DATE('2023-04-01', 'YYYY-MM-DD')),
    PARTITION p2023_q2 VALUES LESS THAN (TO_DATE('2023-07-01', 'YYYY-MM-DD')),
    PARTITION p2023_q3 VALUES LESS THAN (TO_DATE('2023-10-01', 'YYYY-MM-DD')),
    PARTITION p2023_q4 VALUES LESS THAN (TO_DATE('2024-01-01', 'YYYY-MM-DD'))
);

列表分区

  • 定义:基于列值的离散列表进行分区
  • 适用场景:按地区、状态、类型等分类数据
  • 示例
sql
CREATE TABLE employees (
    emp_id NUMBER,
    emp_name VARCHAR2(100),
    department VARCHAR2(50)
)
PARTITION BY LIST (department) (
    PARTITION p_sales VALUES ('SALES', 'MARKETING'),
    PARTITION p_tech VALUES ('IT', 'DEVELOPMENT', 'SUPPORT'),
    PARTITION p_admin VALUES ('HR', 'FINANCE', 'ADMIN')
);

哈希分区

  • 定义:基于列值的哈希值进行分区
  • 适用场景:数据分布均匀、无明显范围或列表特征
  • 示例
sql
CREATE TABLE large_table (
    id NUMBER,
    data VARCHAR2(1000)
)
PARTITION BY HASH (id) (
    PARTITION p1,
    PARTITION p2,
    PARTITION p3,
    PARTITION p4
);

复合分区

  • 范围-哈希分区:先按范围分区,再按哈希分区
  • 范围-列表分区:先按范围分区,再按列表分区
  • 示例
sql
CREATE TABLE sales_detail (
    sale_id NUMBER,
    sale_date DATE,
    region VARCHAR2(50),
    amount NUMBER
)
PARTITION BY RANGE (sale_date)
SUBPARTITION BY LIST (region) (
    PARTITION p2023 VALUES LESS THAN (TO_DATE('2024-01-01', 'YYYY-MM-DD')) (
        SUBPARTITION p2023_north VALUES ('NORTH'),
        SUBPARTITION p2023_south VALUES ('SOUTH'),
        SUBPARTITION p2023_east VALUES ('EAST'),
        SUBPARTITION p2023_west VALUES ('WEST')
    ),
    PARTITION p2024 VALUES LESS THAN (TO_DATE('2025-01-01', 'YYYY-MM-DD')) (
        SUBPARTITION p2024_north VALUES ('NORTH'),
        SUBPARTITION p2024_south VALUES ('SOUTH'),
        SUBPARTITION p2024_east VALUES ('EAST'),
        SUBPARTITION p2024_west VALUES ('WEST')
    )
);

分区表创建

基本创建语法

sql
CREATE TABLE table_name (
    column1 datatype,
    column2 datatype,
    ...
)
PARTITION BY {RANGE | LIST | HASH} (partitioning_column)
(PARTITION partition_name VALUES {LESS THAN (value) | IN (value_list) | ...},
 PARTITION partition_name VALUES {LESS THAN (value) | IN (value_list) | ...},
 ...
);

分区表参数设置

  • 分区表空间
sql
CREATE TABLE sales (
    sale_id NUMBER,
    sale_date DATE,
    amount NUMBER
)
PARTITION BY RANGE (sale_date) (
    PARTITION p2023 VALUES LESS THAN (TO_DATE('2024-01-01', 'YYYY-MM-DD')) TABLESPACE ts_sales_2023,
    PARTITION p2024 VALUES LESS THAN (TO_DATE('2025-01-01', 'YYYY-MM-DD')) TABLESPACE ts_sales_2024
);
  • 索引分区
sql
-- 本地分区索引
CREATE INDEX idx_sales_date ON sales(sale_date) LOCAL;

-- 全局分区索引
CREATE INDEX idx_sales_amount ON sales(amount) GLOBAL
    PARTITION BY RANGE (amount) (
        PARTITION p_amount_1 VALUES LESS THAN (1000),
        PARTITION p_amount_2 VALUES LESS THAN (10000),
        PARTITION p_amount_3 VALUES LESS THAN (MAXVALUE)
    );

分区管理操作

添加分区

  • 范围分区
sql
ALTER TABLE sales ADD PARTITION p2025 VALUES LESS THAN (TO_DATE('2026-01-01', 'YYYY-MM-DD'));
  • 列表分区
sql
ALTER TABLE employees ADD PARTITION p_other VALUES ('OTHER');
  • 哈希分区
sql
ALTER TABLE large_table ADD PARTITION p5;

删除分区

  • 删除分区及数据
sql
ALTER TABLE sales DROP PARTITION p2023;
  • 仅删除分区数据
sql
ALTER TABLE sales TRUNCATE PARTITION p2023;

合并分区

sql
ALTER TABLE sales MERGE PARTITIONS p2023_q1, p2023_q2 INTO PARTITION p2023_h1;

拆分分区

sql
ALTER TABLE sales SPLIT PARTITION p2023_at INTO (
    PARTITION p2023_jan_feb VALUES LESS THAN (TO_DATE('2023-03-01', 'YYYY-MM-DD')),
    PARTITION p2023_mar VALUES LESS THAN (TO_DATE('2023-04-01', 'YYYY-MM-DD'))
);

重命名分区

sql
ALTER TABLE sales RENAME PARTITION p2023 TO p2023_old;

移动分区

sql
ALTER TABLE sales MOVE PARTITION p2024 TABLESPACE ts_sales_new;

分区表维护

分区统计信息

  • 收集分区统计信息
sql
-- 收集单个分区统计信息
EXEC DBMS_STATS.GATHER_TABLE_STATS(
    ownname => 'SCOTT',
    tabname => 'SALES',
    partname => 'P2023',
    estimate_percent => DBMS_STATS.AUTO_SAMPLE_SIZE,
    method_opt => 'FOR ALL COLUMNS SIZE AUTO'
);

-- 收集整个分区表统计信息
EXEC DBMS_STATS.GATHER_TABLE_STATS(
    ownname => 'SCOTT',
    tabname => 'SALES',
    estimate_percent => DBMS_STATS.AUTO_SAMPLE_SIZE,
    method_opt => 'FOR ALL COLUMNS SIZE AUTO',
    granularity => 'GLOBAL AND PARTITION'
);

分区索引维护

  • 重建分区索引
sql
-- 重建本地分区索引
ALTER INDEX idx_sales_date REBUILD PARTITION p2023;

-- 重建全局分区索引
ALTER INDEX idx_sales_amount REBUILD PARTITION p_amount_1;
  • 维护索引状态
sql
-- 检查索引分区状态
SELECT index_name, partition_name, status 
FROM dba_ind_partitions 
WHERE index_owner = 'SCOTT' AND index_name = 'IDX_SALES_DATE';

-- 重建不可用索引
ALTER INDEX idx_sales_date REBUILD UNUSABLE PARTITIONS;

分区数据维护

  • 分区数据加载
sql
-- 使用 INSERT INTO SELECT 加载数据
INSERT INTO sales PARTITION (p2024) 
SELECT * FROM staging_sales WHERE sale_date >= TO_DATE('2024-01-01', 'YYYY-MM-DD');

-- 使用并行加载
ALTER SESSION ENABLE PARALLEL DML;
INSERT /*+ PARALLEL(sales, 4) */ INTO sales PARTITION (p2024) 
SELECT * FROM staging_sales WHERE sale_date >= TO_DATE('2024-01-01', 'YYYY-MM-DD');
  • 分区数据归档
sql
-- 将旧分区数据导出到归档表
CREATE TABLE sales_archive AS 
SELECT * FROM sales PARTITION (p2022);

-- 截断旧分区
ALTER TABLE sales TRUNCATE PARTITION p2022;

分区表性能优化

查询优化

  • 分区裁剪
sql
-- 利用分区裁剪,只扫描相关分区
SELECT * FROM sales 
WHERE sale_date BETWEEN TO_DATE('2023-01-01', 'YYYY-MM-DD') 
                   AND TO_DATE('2023-03-31', 'YYYY-MM-DD');
  • 并行查询
sql
-- 启用并行查询
ALTER SESSION ENABLE PARALLEL DML;

SELECT /*+ PARALLEL(sales, 4) */ * FROM sales 
WHERE sale_date BETWEEN TO_DATE('2023-01-01', 'YYYY-MM-DD') 
                   AND TO_DATE('2023-12-31', 'YYYY-MM-DD');

索引优化

  • 本地分区索引:与表分区结构相同,维护成本低
  • 全局分区索引:可以跨分区范围,查询性能可能更好
  • 位图索引:适合低基数列,在分区表上效果良好

存储优化

  • 分区表空间分离:将不同分区存储在不同表空间
  • 压缩分区:对不常修改的分区启用压缩
sql
-- 创建压缩分区
CREATE TABLE sales (
    sale_id NUMBER,
    sale_date DATE,
    amount NUMBER
)
PARTITION BY RANGE (sale_date) (
    PARTITION p2023 VALUES LESS THAN (TO_DATE('2024-01-01', 'YYYY-MM-DD')) COMPRESS,
    PARTITION p2024 VALUES LESS THAN (TO_DATE('2025-01-01', 'YYYY-MM-DD'))
);

-- 为现有分区启用压缩
ALTER TABLE sales MOVE PARTITION p2023 COMPRESS;

分区表监控

分区使用情况

  • 查询分区大小
sql
SELECT segment_name, partition_name, bytes/1024/1024 AS size_mb 
FROM dba_segments 
WHERE owner = 'SCOTT' AND segment_name = 'SALES' 
ORDER BY partition_name;
  • 查询分区行数
sql
SELECT table_name, partition_name, num_rows 
FROM dba_tab_partitions 
WHERE table_owner = 'SCOTT' AND table_name = 'SALES' 
ORDER BY partition_name;

分区性能监控

  • 分区访问统计
sql
-- 启用分区级统计
ALTER TABLE sales MONITORING USAGE;

-- 查询分区访问情况
SELECT * FROM v$partition_usage WHERE owner = 'SCOTT' AND table_name = 'SALES';
  • 执行计划分析
sql
EXPLAIN PLAN FOR
SELECT * FROM sales 
WHERE sale_date BETWEEN TO_DATE('2023-01-01', 'YYYY-MM-DD') 
                   AND TO_DATE('2023-03-31', 'YYYY-MM-DD');

SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY);

常见分区问题和解决方案

分区键选择不当

  • 症状:查询性能不佳、数据分布不均匀
  • 原因:分区键选择不合适,导致分区裁剪效果差
  • 解决方案
    • 重新评估分区键选择
    • 考虑复合分区策略
    • 针对特定查询模式优化分区设计

分区数量过多

  • 症状:管理复杂、元数据开销大
  • 原因:创建了过多的分区
  • 解决方案
    • 合并过小的分区
    • 重新设计分区策略
    • 考虑使用子分区替代多个顶级分区

分区索引失效

  • 症状:查询性能下降、索引状态为 UNUSABLE
  • 原因:分区维护操作导致索引失效
  • 解决方案
    • 在分区维护时使用 UPDATE INDEXES 子句
    • 定期检查并重建失效索引
    • 考虑使用本地分区索引减少失效风险

分区统计信息过时

  • 症状:执行计划不佳、查询性能下降
  • 原因:分区统计信息过时,导致优化器做出错误决策
  • 解决方案
    • 建立定期收集统计信息的作业
    • 在数据变更后及时收集统计信息
    • 使用自动统计信息收集功能

版本差异

Oracle 11g

  • 支持基本的分区类型:范围、列表、哈希、复合
  • 分区表管理功能相对基础
  • 统计信息收集功能有限
  • 对大分区表的支持有限

Oracle 12c

  • 引入了区间分区(Interval Partitioning)
  • 支持在线分区维护操作
  • 增强了分区统计信息收集
  • 引入了引用分区
  • 支持更多分区类型和子分区策略

Oracle 19c/21c

  • 进一步增强了分区功能
  • 支持自动分区维护
  • 改进了分区表的并行处理
  • 提供了更详细的分区监控视图
  • 优化了分区表的存储和访问效率

最佳实践

  • 分区键选择:选择查询中常用的列,确保数据均匀分布
  • 分区大小:保持分区大小合理,避免过大或过小
  • 分区数量:根据管理能力和性能需求确定合适的分区数量
  • 索引策略:本地分区索引适合大多数场景,全局分区索引用于特殊查询需求
  • 统计信息:定期收集分区统计信息,确保优化器做出正确决策
  • 维护计划:制定定期的分区维护计划,包括数据归档、分区合并等
  • 监控预警:建立分区表使用情况的监控和预警机制
  • 测试验证:在生产环境实施前充分测试分区策略

常见问题(FAQ)

Q1: 如何选择合适的分区类型?

A1: 分区类型选择建议:

  • 时间序列数据:使用范围分区
  • 分类数据:使用列表分区
  • 均匀分布数据:使用哈希分区
  • 复杂查询模式:使用复合分区

Q2: 分区表和索引组织表 (IOT) 可以结合使用吗?

A2: 是的,Oracle 支持分区索引组织表:

sql
CREATE TABLE sales_iot (
    sale_id NUMBER PRIMARY KEY,
    sale_date DATE,
    amount NUMBER
)
ORGANIZATION INDEX
PARTITION BY RANGE (sale_date) (
    PARTITION p2023 VALUES LESS THAN (TO_DATE('2024-01-01', 'YYYY-MM-DD')),
    PARTITION p2024 VALUES LESS THAN (TO_DATE('2025-01-01', 'YYYY-MM-DD'))
);

Q3: 如何将非分区表转换为分区表?

A3: 可以使用以下方法:

  • 使用 CTAS 方法
sql
CREATE TABLE sales_partitioned (
    sale_id NUMBER,
    sale_date DATE,
    amount NUMBER
)
PARTITION BY RANGE (sale_date) (
    PARTITION p2023 VALUES LESS THAN (TO_DATE('2024-01-01', 'YYYY-MM-DD')),
    PARTITION p2024 VALUES LESS THAN (TO_DATE('2025-01-01', 'YYYY-MM-DD'))
) AS
SELECT * FROM sales;

-- 重命名表
RENAME sales TO sales_old;
RENAME sales_partitioned TO sales;
  • 使用 DBMS_REDEFINITION:支持在线转换

Q4: 分区表对备份和恢复有什么影响?

A4: 分区表对备份和恢复的影响:

  • 优势:可以按分区进行备份,减少备份时间和存储空间
  • 恢复:可以单独恢复特定分区,提高恢复灵活性
  • 注意:全局分区索引在分区恢复后可能需要重建

Q5: 如何监控分区表的空间使用情况?

A5: 可以使用以下查询监控分区表空间:

sql
SELECT 
    t.table_name,
    p.partition_name,
    p.tablespace_name,
    ROUND(p.bytes/1024/1024, 2) AS size_mb,
    ROUND(p.blocks*8/1024, 2) AS blocks_mb,
    p.num_rows
FROM 
    dba_tab_partitions p,
    dba_tables t
WHERE 
    p.table_owner = t.owner
    AND p.table_name = t.table_name
    AND t.owner = 'SCOTT'
    AND t.table_name = 'SALES'
ORDER BY 
    p.partition_name;