范围哈希组合分区适合「按时间归档 + 在每个时间段内均匀分布数据」的场景,如销售表、日志表、订单表;但需严格遵循语法规范、键正交、子分区数为2的幂,并确保查询带一级分区键以触发剪枝。

直接说结论:范围哈希组合分区适合「按时间归档 + 在每个时间段内均匀分布数据」的场景,比如销售表、日志表、订单表;但设计不当会导致子分区倾斜、查询失效、维护复杂。
范围哈希组合分区的建表语法必须显式写出所有子分区
Oracle 不支持像 PARTITIONS 4 这样的简写——你得手动列出每个一级分区及其下的全部子分区名。漏写、错名、少定义都会报 ORA-14019 或 ORA-14037。
常见错误现象:
- 只写
SUBPARTITIONS 2但没在每个PARTITION块里展开子分区定义 → 报错 - 一级分区用了
VALUES LESS THAN,但二级子分区名重复(如两个sub_p1)→ 报错 - 子分区没指定表空间,导致全落在默认表空间,I/O 热点集中
正确写法示例(带表空间):
CREATE TABLE sales_log (
sale_date DATE NOT NULL,
cust_id NUMBER,
amount NUMBER
) PARTITION BY RANGE (sale_date)
SUBPARTITION BY HASH (cust_id) SUBPARTITIONS 4 (
PARTITION p_2024 VALUES LESS THAN (TO_DATE('2025-01-01','YYYY-MM-DD'))
(SUBPARTITION p_2024_s1 TABLESPACE ts_sales_2024_1,
SUBPARTITION p_2024_s2 TABLESPACE ts_sales_2024_2,
SUBPARTITION p_2024_s3 TABLESPACE ts_sales_2024_3,
SUBPARTITION p_2024_s4 TABLESPACE ts_sales_2024_4),
PARTITION p_2025 VALUES LESS THAN (TO_DATE('2026-01-01','YYYY-MM-DD'))
(SUBPARTITION p_2025_s1 TABLESPACE ts_sales_2025_1,
SUBPARTITION p_2025_s2 TABLESPACE ts_sales_2025_2,
SUBPARTITION p_2025_s3 TABLESPACE ts_sales_2025_3,
SUBPARTITION p_2025_s4 TABLESPACE ts_sales_2025_4)
);
一级分区键和二级分区键必须语义正交,不能强相关
如果一级用 sale_date,二级再用 EXTRACT(YEAR FROM sale_date),那哈希就失效了——所有同一年的数据都映射到同一个子分区,等于白分。
使用场景判断:
- ✅ 合理:一级
sale_date(时间维度),二级cust_id(高唯一性 ID) - ❌ 危险:一级
region_code,二级city_code(城市必然属于某区域,哈希结果高度耦合) - ⚠️ 可行但需绕路:二级键只有低基数列?先建虚拟列:
ALTER TABLE t ADD (cust_hash AS MOD(cust_id, 1000)),再按该列哈希
Oracle 不支持在 PARTITION BY HASH 中直接写表达式,虚拟列是唯一合规解法。
子分区数量必须是 2 的幂,且一级分区数不宜过多
哈希子分区数不是“越多越好”。SUBPARTITIONS 6 或 12 会导致数据分布不均——Oracle 内部用位运算优化模运算,非 2 的幂会退化为除法,还可能触发隐式重映射。
性能与维护权衡:
- 推荐子分区数:4 / 8 / 16(兼顾均匀性与管理成本)
- 一级分区数建议 ≤ 20,否则 DDL 操作(如
ADD PARTITION)耗时显著上升 - 别指望靠增加子分区数解决热点:若
cust_id本身有大量重复(比如测试数据全填 1),哈希再均匀也无济于事
扩容时注意:从 SUBPARTITIONS 4 改成 8,必须重建整个一级分区(EXCHANGE 或 MOVE),无法在线完成。
查询必须带上一级分区键才能触发分区剪枝,二级键仅对等值有效
WHERE sale_date = DATE '2025-06-01' AND cust_id = 12345 能精准定位 1 个子分区;但 WHERE cust_id = 12345(缺一级键)会扫所有一级分区下的全部子分区——性能崩盘。
容易被忽略的细节:
- 时间条件必须能静态推导:
WHERE sale_date BETWEEN SYSDATE - 30 AND SYSDATE多数情况下仍可剪枝;但WHERE sale_date > ADD_MONTHS(SYSDATE, -1)在某些 Oracle 版本中可能失效 -
IN列表如果跨多个一级分区(如sale_date IN (DATE '2024-12-01', DATE '2025-01-15')),会访问对应分区,但子分区仍需全扫各自范围 - 任何涉及函数的分区键(如
TRUNC(sale_date))都会让剪枝失效,必须用原始列
真正麻烦的不是建表,而是后续的维护节奏:每年新增一级分区、定期归档旧分区、监控子分区数据倾斜——这些动作没法自动化,全靠 DBA 手动执行脚本或调度任务。











