散列分区要求分区数为2的幂(如2、4、8等),因其底层采用hash(key) & (numPartitions−1)位运算实现分区定位,仅当numPartitions为2的幂时该运算才等价于取模且能保证数据均匀分布。
oracle 11g 中 hash 分区数必须是 2 的幂次方(如 2、4、8、16、32),否则数据分布会严重倾斜——这不是 oracle 的“建议”,而是其哈希算法实现决定的硬性约束。
为什么 Oracle Hash 分区数必须是 2 的幂次方
Oracle 底层用的是 hash(key) & (num_partitions - 1) 计算分区号,这等价于取模运算 hash(key) % num_partitions,但仅当 num_partitions 是 2 的幂时才成立。若设为 12 或 18,位运算结果会集中在低位,导致前几个分区堆积大量数据,后几个长期空闲。
常见错误现象:
-
dba_segments中看到某些分区段大小是其他分区的 5–10 倍 - 执行
select dbms_rowid.rowid_object(rowid), count(*) from your_hash_table group by dbms_rowid.rowid_object(rowid)返回明显不均的对象 ID 计数 - 增删分区后,新增分区几乎没数据,而某个旧分区仍持续膨胀
建表时如何正确指定 Hash 分区数量
直接在 CREATE TABLE ... PARTITION BY HASH 语句中写死 2 的幂,不要动态计算或四舍五入。
- ✅ 正确:
PARTITIONS 16、PARTITIONS 32 - ❌ 危险:
PARTITIONS 12(哪怕你算出“平均每个分区 8 万行”)、PARTITIONS CEIL(100000/10000)(SQL 不支持表达式) - ⚠️ 注意:Oracle 不允许在
PARTITIONS后跟子查询或函数,只能是字面整数
示例:
CREATE TABLE sales_hash ( id NUMBER, region VARCHAR2(10), amount NUMBER ) PARTITION BY HASH(id) PARTITIONS 16;
已有非 2 的幂分区表怎么补救
Oracle 不支持直接修改分区数,必须重建。但可以避免全量导出导入:
- 用
DBMS_REDEFINITION在线重定义:先建新表(PARTITIONS 16),再启动重定义流程,期间原表可读写 - 如果停机窗口允许,更简单的方式是
CREATE TABLE AS SELECT+RENAME+ 索引/约束重建 - 切勿尝试
ALTER TABLE ... ADD PARTITION来“凑够 16 个”——Hash 分区不支持 ADD,只支持 SPLIT(且 SPLIT 只能分裂现有分区,无法修正整体分布逻辑)
特别注意:ADD PARTITION 是范围分区/列表分区的操作,对 Hash 分区无效,执行会报错 ORA-14225: ADD PARTITION not supported for hash partitioned objects。
选 8 还是 16 或 32?关键看 key 基数和写入压力
没有通用最优值,但有明确取舍逻辑:
- key 基数低(比如只有几十个固定值)→ 选小值,如
8,否则大量分区为空,浪费空间和管理开销 - 单日写入超千万行、且 key 分布较散 → 优先
16或32,缓解单分区 I/O 和锁竞争 - 别盲目上
64:Oracle 每个分区对应独立段(segment),过多分区会显著增加dba_segments元数据、SMON 清理负担,以及 RMAN 备份粒度复杂度
验证是否生效的最简方式:查 user_tab_partitions 确认 partition_name 数量是 2 的幂,再用 dbms_rowid.rowid_object 统计各分区对象 ID 分布,应大致均匀(±15% 内可接受)。
真正容易被忽略的点是:即使分区数合规,若哈希键列本身长尾(例如 5% 的 region 值占 80% 的记录),倾斜仍会发生——这时得换键、加 salt,或者改用复合分区,而不是继续调分区数。











