Skip to content

Oracle 分区表与分区索引

分区表与分区索引是 Oracle 数据库中用于管理大型表和索引的重要技术。它们可以提高查询性能、简化管理和维护、提高可用性,是处理海量数据的有效手段。

分区概述

分区的定义

分区是将大型表或索引分解为更小、更易管理的片段的过程。每个片段称为一个分区,分区之间是逻辑上相关的,但物理上是独立的。

分区的作用

  • 提高查询性能:只需要访问相关分区,不需要扫描整个表
  • 简化管理和维护:可以单独管理和维护每个分区
  • 提高可用性:一个分区损坏不会影响其他分区
  • 提高并行性能:可以并行处理多个分区
  • 优化存储:可以将不同分区存储在不同的表空间和磁盘上

分区的类型

分区类型特点适用场景
范围分区(Range Partitioning)基于列的范围值进行分区日期、时间、数值范围等
列表分区(List Partitioning)基于列的离散值进行分区地区、状态、类型等
哈希分区(Hash Partitioning)基于哈希函数的值进行分区随机分布数据,均衡负载
复合分区(Composite Partitioning)结合两种或多种分区类型复杂业务场景
间隔分区(Interval Partitioning)自动根据范围创建分区日期、时间等连续数据
引用分区(Reference Partitioning)基于父表的分区键进行分区主从表关系
系统分区(System Partitioning)由用户指定分区自定义分区策略

分区表管理

范围分区(Range Partitioning)

特点

  • 基于列的范围值进行分区
  • 每个分区包含一个连续的范围值
  • 适合日期、时间、数值范围等

创建方法

sql
-- 按日期范围分区
CREATE TABLE sales (
  sale_id      NUMBER(10) PRIMARY KEY,
  sale_date    DATE NOT NULL,
  product_id   NUMBER(6) NOT NULL,
  quantity     NUMBER(8) NOT NULL,
  amount       NUMBER(12,2) NOT NULL
)
PARTITION BY RANGE (sale_date) (
  PARTITION sales_q1_2024 VALUES LESS THAN (TO_DATE('2024-04-01', 'YYYY-MM-DD')) TABLESPACE sales_q1,
  PARTITION sales_q2_2024 VALUES LESS THAN (TO_DATE('2024-07-01', 'YYYY-MM-DD')) TABLESPACE sales_q2,
  PARTITION sales_q3_2024 VALUES LESS THAN (TO_DATE('2024-10-01', 'YYYY-MM-DD')) TABLESPACE sales_q3,
  PARTITION sales_q4_2024 VALUES LESS THAN (TO_DATE('2025-01-01', 'YYYY-MM-DD')) TABLESPACE sales_q4,
  PARTITION sales_future VALUES LESS THAN (MAXVALUE) TABLESPACE sales_future
);

-- 按数值范围分区
CREATE TABLE employees (
  employee_id   NUMBER(6) PRIMARY KEY,
  first_name    VARCHAR2(20),
  last_name     VARCHAR2(25) NOT NULL,
  salary        NUMBER(8,2) NOT NULL
)
PARTITION BY RANGE (salary) (
  PARTITION p1 VALUES LESS THAN (5000) TABLESPACE emp_p1,
  PARTITION p2 VALUES LESS THAN (10000) TABLESPACE emp_p2,
  PARTITION p3 VALUES LESS THAN (15000) TABLESPACE emp_p3,
  PARTITION p4 VALUES LESS THAN (MAXVALUE) TABLESPACE emp_p4
);

列表分区(List Partitioning)

特点

  • 基于列的离散值进行分区
  • 每个分区包含一个或多个离散值
  • 适合地区、状态、类型等

创建方法

sql
-- 按地区列表分区
CREATE TABLE customers (
  customer_id   NUMBER(10) PRIMARY KEY,
  customer_name VARCHAR2(100) NOT NULL,
  region        VARCHAR2(20) NOT NULL,
  status        VARCHAR2(10) NOT NULL
)
PARTITION BY LIST (region) (
  PARTITION p_north VALUES ('NORTH', 'NORTHEAST') TABLESPACE cust_north,
  PARTITION p_south VALUES ('SOUTH', 'SOUTHEAST') TABLESPACE cust_south,
  PARTITION p_west VALUES ('WEST') TABLESPACE cust_west,
  PARTITION p_central VALUES ('CENTRAL') TABLESPACE cust_central,
  PARTITION p_other VALUES (DEFAULT) TABLESPACE cust_other
);

-- 按状态列表分区
CREATE TABLE orders (
  order_id     NUMBER(10) PRIMARY KEY,
  order_date   DATE NOT NULL,
  customer_id  NUMBER(10) NOT NULL,
  status       VARCHAR2(15) NOT NULL
)
PARTITION BY LIST (status) (
  PARTITION p_pending VALUES ('PENDING') TABLESPACE ord_pending,
  PARTITION p_processing VALUES ('PROCESSING') TABLESPACE ord_processing,
  PARTITION p_shipped VALUES ('SHIPPED') TABLESPACE ord_shipped,
  PARTITION p_delivered VALUES ('DELIVERED') TABLESPACE ord_delivered,
  PARTITION p_cancelled VALUES ('CANCELLED') TABLESPACE ord_cancelled
);

哈希分区(Hash Partitioning)

特点

  • 基于哈希函数的值进行分区
  • 数据均匀分布在各个分区
  • 适合随机分布数据,均衡负载

创建方法

sql
-- 按 customer_id 哈希分区
CREATE TABLE order_items (
  order_item_id NUMBER(12) PRIMARY KEY,
  order_id      NUMBER(10) NOT NULL,
  product_id    NUMBER(6) NOT NULL,
  quantity      NUMBER(8) NOT NULL,
  unit_price    NUMBER(10,2) NOT NULL
)
PARTITION BY HASH (customer_id) PARTITIONS 8
STORE IN (ord_items_p1, ord_items_p2, ord_items_p3, ord_items_p4, ord_items_p5, ord_items_p6, ord_items_p7, ord_items_p8);

-- 自定义哈希分区表空间
CREATE TABLE products (
  product_id   NUMBER(6) PRIMARY KEY,
  product_name VARCHAR2(100) NOT NULL,
  category_id  NUMBER(4) NOT NULL,
  price        NUMBER(10,2) NOT NULL
)
PARTITION BY HASH (product_id) (
  PARTITION p1 TABLESPACE prod_p1,
  PARTITION p2 TABLESPACE prod_p2,
  PARTITION p3 TABLESPACE prod_p3,
  PARTITION p4 TABLESPACE prod_p4
);

复合分区(Composite Partitioning)

特点

  • 结合两种或多种分区类型
  • 适合复杂业务场景
  • 支持范围-列表、范围-哈希、列表-哈希等组合

创建方法

sql
-- 范围-列表复合分区
CREATE TABLE sales_composite (
  sale_id      NUMBER(10) PRIMARY KEY,
  sale_date    DATE NOT NULL,
  region       VARCHAR2(20) NOT NULL,
  product_id   NUMBER(6) NOT NULL,
  quantity     NUMBER(8) NOT NULL,
  amount       NUMBER(12,2) NOT NULL
)
PARTITION BY RANGE (sale_date)
SUBPARTITION BY LIST (region) (
  PARTITION p_q1_2024 VALUES LESS THAN (TO_DATE('2024-04-01', 'YYYY-MM-DD')) (
    SUBPARTITION p_q1_2024_north VALUES ('NORTH', 'NORTHEAST'),
    SUBPARTITION p_q1_2024_south VALUES ('SOUTH', 'SOUTHEAST'),
    SUBPARTITION p_q1_2024_west VALUES ('WEST'),
    SUBPARTITION p_q1_2024_central VALUES ('CENTRAL')
  ),
  PARTITION p_q2_2024 VALUES LESS THAN (TO_DATE('2024-07-01', 'YYYY-MM-DD')) (
    SUBPARTITION p_q2_2024_north VALUES ('NORTH', 'NORTHEAST'),
    SUBPARTITION p_q2_2024_south VALUES ('SOUTH', 'SOUTHEAST'),
    SUBPARTITION p_q2_2024_west VALUES ('WEST'),
    SUBPARTITION p_q2_2024_central VALUES ('CENTRAL')
  )
);

-- 范围-哈希复合分区
CREATE TABLE orders_composite (
  order_id     NUMBER(10) PRIMARY KEY,
  order_date   DATE NOT NULL,
  customer_id  NUMBER(10) NOT NULL,
  amount       NUMBER(12,2) NOT NULL
)
PARTITION BY RANGE (order_date)
SUBPARTITION BY HASH (customer_id) SUBPARTITIONS 4 (
  PARTITION p_2024 VALUES LESS THAN (TO_DATE('2025-01-01', 'YYYY-MM-DD')),
  PARTITION p_2025 VALUES LESS THAN (TO_DATE('2026-01-01', 'YYYY-MM-DD'))
);

间隔分区(Interval Partitioning)

特点

  • 自动根据范围创建分区
  • 适合日期、时间等连续数据
  • Oracle 11g 及以上版本支持

创建方法

sql
-- 按月份自动创建间隔分区
CREATE TABLE sales_interval (
  sale_id      NUMBER(10) PRIMARY KEY,
  sale_date    DATE NOT NULL,
  product_id   NUMBER(6) NOT NULL,
  quantity     NUMBER(8) NOT NULL,
  amount       NUMBER(12,2) NOT NULL
)
PARTITION BY RANGE (sale_date)
INTERVAL (NUMTOYMINTERVAL(1, 'MONTH')) (
  PARTITION p_initial VALUES LESS THAN (TO_DATE('2024-01-01', 'YYYY-MM-DD'))
);

-- 按天自动创建间隔分区
CREATE TABLE logs_interval (
  log_id       NUMBER(12) PRIMARY KEY,
  log_date     DATE NOT NULL,
  log_level    VARCHAR2(10) NOT NULL,
  message      CLOB NOT NULL
)
PARTITION BY RANGE (log_date)
INTERVAL (NUMTODSINTERVAL(1, 'DAY')) (
  PARTITION p_initial VALUES LESS THAN (TO_DATE('2024-01-01', 'YYYY-MM-DD'))
);

引用分区(Reference Partitioning)

特点

  • 基于父表的分区键进行分区
  • 主从表关系,从表的分区与父表保持一致
  • Oracle 11g 及以上版本支持

创建方法

sql
-- 创建父表(范围分区)
CREATE TABLE orders_ref (
  order_id     NUMBER(10) PRIMARY KEY,
  order_date   DATE NOT NULL,
  customer_id  NUMBER(10) NOT NULL,
  status       VARCHAR2(15) NOT NULL
)
PARTITION BY RANGE (order_date) (
  PARTITION p_2024 VALUES LESS THAN (TO_DATE('2025-01-01', 'YYYY-MM-DD')),
  PARTITION p_2025 VALUES LESS THAN (TO_DATE('2026-01-01', 'YYYY-MM-DD'))
);

-- 创建从表(引用分区)
CREATE TABLE order_items_ref (
  order_item_id NUMBER(12) PRIMARY KEY,
  order_id      NUMBER(10) NOT NULL,
  product_id    NUMBER(6) NOT NULL,
  quantity      NUMBER(8) NOT NULL,
  unit_price    NUMBER(10,2) NOT NULL,
  CONSTRAINT fk_order_items_ref FOREIGN KEY (order_id) REFERENCES orders_ref(order_id)
)
PARTITION BY REFERENCE (fk_order_items_ref);

分区索引管理

局部分区索引(Local Partitioned Index)

特点

  • 索引分区与表分区一一对应
  • 每个索引分区只包含对应表分区的数据
  • 维护简单,自动随表分区变化
  • 适合与表分区相同的查询模式

创建方法

sql
-- 创建局部分区索引
CREATE INDEX sales_product_id_local_idx ON sales(product_id) LOCAL;

-- 创建唯一局部分区索引(必须包含分区键)
CREATE UNIQUE INDEX sales_sale_id_local_idx ON sales(sale_id, sale_date) LOCAL;

全局分区索引(Global Partitioned Index)

特点

  • 索引分区与表分区不一一对应
  • 可以使用与表不同的分区键
  • 维护复杂,表分区变化可能导致索引失效
  • 适合全局查询

创建方法

sql
-- 创建全局分区索引
CREATE INDEX sales_customer_id_global_idx ON sales(customer_id)
GLOBAL PARTITION BY RANGE (customer_id) (
  PARTITION p1 VALUES LESS THAN (1000),
  PARTITION p2 VALUES LESS THAN (2000),
  PARTITION p3 VALUES LESS THAN (3000),
  PARTITION p4 VALUES LESS THAN (MAXVALUE)
);

-- 创建全局唯一索引
CREATE UNIQUE INDEX sales_order_id_global_idx ON sales(order_id)
GLOBAL PARTITION BY RANGE (order_id) (
  PARTITION p1 VALUES LESS THAN (10000),
  PARTITION p2 VALUES LESS THAN (20000),
  PARTITION p3 VALUES LESS THAN (MAXVALUE)
);

全局非分区索引(Global Nonpartitioned Index)

特点

  • 整个索引作为一个整体,不分区
  • 适合需要全局访问的场景

创建方法

sql
-- 创建全局非分区索引
CREATE INDEX sales_product_id_global_idx ON sales(product_id) GLOBAL;

-- 创建全局唯一非分区索引
CREATE UNIQUE INDEX sales_sale_id_global_idx ON sales(sale_id) GLOBAL;

分区维护

分区的添加、删除、合并和拆分

sql
-- 添加分区
ALTER TABLE sales ADD PARTITION sales_2025 VALUES LESS THAN (TO_DATE('2026-01-01', 'YYYY-MM-DD')) TABLESPACE sales_2025;

-- 删除分区(会删除分区中的数据)
ALTER TABLE sales DROP PARTITION sales_2023;

-- 删除分区但保留数据
ALTER TABLE sales TRUNCATE PARTITION sales_2023;

-- 合并分区
ALTER TABLE sales MERGE PARTITIONS sales_q1_2024, sales_q2_2024 INTO PARTITION sales_h1_2024;

-- 拆分分区
ALTER TABLE sales SPLIT PARTITION sales_h1_2024 AT (TO_DATE('2024-04-01', 'YYYY-MM-DD')) INTO (PARTITION sales_q1_2024, PARTITION sales_q2_2024);

-- 交换分区(将分区与表交换)
CREATE TABLE sales_q1_2024_temp AS SELECT * FROM sales WHERE sale_date < TO_DATE('2024-04-01', 'YYYY-MM-DD');
ALTER TABLE sales EXCHANGE PARTITION sales_q1_2024 WITH TABLE sales_q1_2024_temp;

-- 移动分区(更改分区的表空间)
ALTER TABLE sales MOVE PARTITION sales_q1_2024 TABLESPACE new_sales_tbs;

-- 重命名分区
ALTER TABLE sales RENAME PARTITION sales_q1_2024 TO sales_q1_2024_old;

分区索引的维护

sql
-- 重建局部分区索引
ALTER INDEX sales_product_id_local_idx REBUILD PARTITION sales_q1_2024;

-- 重建所有局部分区索引
ALTER INDEX sales_product_id_local_idx REBUILD PARTITION ALL;

-- 重建全局分区索引
ALTER INDEX sales_customer_id_global_idx REBUILD PARTITION p1;

-- 重建所有全局分区索引
ALTER INDEX sales_customer_id_global_idx REBUILD PARTITION ALL;

-- 修复失效的全局分区索引
ALTER INDEX sales_customer_id_global_idx REBUILD;

-- 查看索引分区状态
SELECT index_name, partition_name, status FROM dba_ind_partitions WHERE index_name = 'SALES_PRODUCT_ID_LOCAL_IDX';

分区表与索引的监控

分区表监控

sql
-- 查看分区表信息
SELECT table_name, partitioning_type, subpartitioning_type, partition_count FROM dba_part_tables;

-- 查看分区信息
SELECT table_name, partition_name, high_value, tablespace_name, num_rows FROM dba_tab_partitions WHERE table_name = 'SALES';

-- 查看子分区信息
SELECT table_name, partition_name, subpartition_name, tablespace_name, num_rows FROM dba_tab_subpartitions WHERE table_name = 'SALES_COMPOSITE';

-- 查看分区表的统计信息
SELECT table_name, partition_name, last_analyzed, num_rows, blocks FROM dba_tab_statistics WHERE table_name = 'SALES';

分区索引监控

sql
-- 查看分区索引信息
SELECT index_name, index_type, partitioning_type, subpartitioning_type, partition_count FROM dba_part_indexes;

-- 查看索引分区信息
SELECT index_name, partition_name, status, tablespace_name FROM dba_ind_partitions WHERE index_name = 'SALES_PRODUCT_ID_LOCAL_IDX';

-- 查看索引子分区信息
SELECT index_name, partition_name, subpartition_name, status, tablespace_name FROM dba_ind_subpartitions WHERE index_name = 'SALES_COMPOSITE_IDX';

-- 查看分区索引的统计信息
SELECT index_name, partition_name, last_analyzed, leaf_blocks, distinct_keys FROM dba_ind_statistics WHERE index_name = 'SALES_PRODUCT_ID_LOCAL_IDX';

最佳实践

分区表最佳实践

  1. 选择合适的分区类型:根据业务需求选择合适的分区类型
  2. 合理设计分区键:选择查询频率高、基数适中的列作为分区键
  3. 控制分区数量:避免创建过多分区,建议每个分区大小在 1GB-10GB 之间
  4. 使用自动分区:对于日期、时间等连续数据,使用间隔分区
  5. 合理设置分区表空间:将不同分区存储在不同的表空间和磁盘上
  6. 定期收集统计信息:确保优化器生成最佳执行计划
  7. 使用本地索引:优先使用本地索引,维护简单,性能良好
  8. 避免全局索引失效:在删除或合并分区时,考虑使用 UPDATE GLOBAL INDEXES 子句

分区索引最佳实践

  1. 优先使用本地索引:维护简单,性能良好
  2. 合理使用全局索引:只在需要全局访问时使用
  3. 避免过度索引:每个索引都会增加 DML 操作的开销
  4. 定期重建索引:对于碎片化严重的索引,定期重建
  5. 使用压缩索引:减少索引的存储空间
  6. 监控索引状态:定期检查索引的状态,及时修复失效的索引

查询优化最佳实践

  1. 使用分区修剪:在查询中包含分区键,让 Oracle 只访问相关分区
  2. 避免跨分区查询:尽量减少跨多个分区的查询
  3. 使用并行查询:对于大型查询,使用并行查询可以提高性能
  4. 使用分区连接:当连接两个分区表时,使用相同的分区键可以提高性能
  5. 使用分区wise join:Oracle 11g 及以上版本支持分区wise join

版本差异

版本特性
Oracle 11g引入间隔分区、引用分区、虚拟列分区
Oracle 12c引入系统分区、增强了间隔分区功能
Oracle 18c引入混合分区、增强了分区维护功能
Oracle 19c引入自动列表分区、增强了分区索引功能
Oracle 23c增强了 JSON 数据的分区支持、优化了分区查询性能

常见问题(FAQ)

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

A1: 根据业务需求选择:

  • 日期、时间等连续数据:范围分区或间隔分区
  • 地区、状态等离散数据:列表分区
  • 随机分布数据:哈希分区
  • 复杂业务场景:复合分区
  • 主从表关系:引用分区

Q2: 如何避免全局索引失效?

A2: 在删除或合并分区时,使用 UPDATE GLOBAL INDEXES 子句:

sql
ALTER TABLE sales DROP PARTITION sales_2023 UPDATE GLOBAL INDEXES;
ALTER TABLE sales MERGE PARTITIONS sales_q1_2024, sales_q2_2024 INTO PARTITION sales_h1_2024 UPDATE GLOBAL INDEXES;

Q3: 如何查看分区表的统计信息?

A3: 使用以下查询:

sql
SELECT table_name, partition_name, last_analyzed, num_rows, blocks FROM dba_tab_statistics WHERE table_name = 'SALES';

Q4: 如何收集分区表的统计信息?

A4: 使用 DBMS_STATS 包:

sql
-- 收集特定分区的统计信息
EXEC DBMS_STATS.GATHER_TABLE_STATS(OWNNAME => 'HR', TABNAME => 'SALES', PARTNAME => 'SALES_Q1_2024', CASCADE => TRUE);

-- 收集所有分区的统计信息
EXEC DBMS_STATS.GATHER_TABLE_STATS(OWNNAME => 'HR', TABNAME => 'SALES', CASCADE => TRUE);

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

A5: 使用以下查询:

sql
-- 查看分区的访问情况
SELECT table_name, partition_name, num_rows, blocks FROM dba_tab_partitions WHERE table_name = 'SALES';

-- 查看分区的 I/O 情况
SELECT * FROM v$partition_statistics WHERE object_name = 'SALES';

Q6: 如何将普通表转换为分区表?

A6: 可以使用以下方法:

  1. 创建分区表,然后插入数据
  2. 使用 DBMS_REDEFINITION 包在线转换
  3. 使用交换分区功能

示例(使用 DBMS_REDEFINITION):

sql
-- 创建分区表
CREATE TABLE sales_partitioned (
  sale_id      NUMBER(10) PRIMARY KEY,
  sale_date    DATE NOT NULL,
  product_id   NUMBER(6) NOT NULL,
  quantity     NUMBER(8) NOT NULL,
  amount       NUMBER(12,2) NOT NULL
)
PARTITION BY RANGE (sale_date) (
  PARTITION sales_2023 VALUES LESS THAN (TO_DATE('2024-01-01', 'YYYY-MM-DD')),
  PARTITION sales_2024 VALUES LESS THAN (TO_DATE('2025-01-01', 'YYYY-MM-DD'))
);

-- 使用 DBMS_REDEFINITION 在线转换
EXEC DBMS_REDEFINITION.START_REDEF_TABLE('HR', 'SALES', 'SALES_PARTITIONED');
EXEC DBMS_REDEFINITION.FINISH_REDEF_TABLE('HR', 'SALES', 'SALES_PARTITIONED');

相关命令速查

命令用途
CREATE TABLE ... PARTITION BY创建分区表
ALTER TABLE ADD PARTITION添加分区
ALTER TABLE DROP PARTITION删除分区
ALTER TABLE TRUNCATE PARTITION清空分区数据
ALTER TABLE MERGE PARTITIONS合并分区
ALTER TABLE SPLIT PARTITION拆分分区
ALTER TABLE EXCHANGE PARTITION交换分区
ALTER TABLE MOVE PARTITION移动分区
CREATE INDEX ... LOCAL创建局部分区索引
CREATE INDEX ... GLOBAL创建全局分区索引
ALTER INDEX REBUILD PARTITION重建索引分区
DBMS_STATS.GATHER_TABLE_STATS收集分区表统计信息
SELECT * FROM dba_part_tables查看分区表信息
SELECT * FROM dba_tab_partitions查看分区信息
SELECT * FROM dba_part_indexes查看分区索引信息
SELECT * FROM dba_ind_partitions查看索引分区信息

总结

分区表与分区索引是 Oracle 数据库中用于管理大型表和索引的重要技术。它们可以提高查询性能、简化管理和维护、提高可用性,是处理海量数据的有效手段。DBA 应该根据实际业务需求选择合适的分区类型和索引类型,合理设计分区键,定期进行监控和维护,以确保分区表和分区索引的高效运行。通过遵循最佳实践,DBA 可以充分发挥分区技术的优势,提高数据库的性能和可靠性。