oracle哈希分区数必须是2的整数次幂(如2、4、8、16),因底层用hash(key) & (n−1)位运算替代取模,仅当n为2的幂时才能等价且保障均匀分布;否则数据严重倾斜、io失衡。

Oracle哈希分区数量不能靠“估”或“凑”,必须是 2 的整数次幂(2、4、8、16、32),否则 hash(key) & (n-1) 位运算会失效,数据立刻倾斜——这不是性能调优问题,是底层映射逻辑崩了。
为什么非得是 2 的幂?
Oracle 不用传统取模 %,而是用位与 & 加速计算:分区号 = hash(id) & (num_partitions - 1)。这个等价于取模的前提,是 num_partitions 必须为 2 的幂。比如 8 → 7 的二进制是 0111,能完整截取 hash 值低 3 位;而 6 → 5 是 0101,高位被屏蔽,大量 hash 值撞到前几个分区。
- 实测
PARTITIONS 6:最小分区行数只有最大分区的 ~40% -
PARTITIONS 12:dba_segments显示某些分区段大小是其他分区的 6–10 倍 - 哪怕你算出“每分区 10 万行”,也绝不能写
PARTITIONS CEIL(1e6/10e4)—— Oracle 不允许表达式,只认字面整数
建表时怎么写才安全?
三处必须显式控制,缺一不可:
- 分区键必须是高基数列,比如
id、user_id;避免用status或全NULL列 - 必须写死
PARTITIONS N,且N是 2 的幂;不能省略、不能靠默认、不能动态生成 - 必须显式指定每个分区的表空间,否则全落到
USERS表空间,IO 又集中了
正确示例:
CREATE TABLE orders ( order_id NUMBER PRIMARY KEY, cust_id NUMBER, amount NUMBER ) PARTITION BY HASH (order_id) PARTITIONS 16 STORE IN (ts_part01, ts_part02, ts_part03, ts_part04);
选 4 还是 32?看真实负载,不是看文档
没有通用最优值,关键看两个硬指标:
-
key 基数:如果
order_id实际不同值 32 就浪费——空分区多,管理开销反升 -
单分区吞吐压力:写入 TPS > 5k 且 key 分布较散,
8容易撑不住;但盲目上128会导致 Flink / Kafka 等下游 consumer fetch 失败、checkpoint 膨胀 - 扩容要重分布:从
8→9或12,所有数据重哈希;必须跳变到下一个 2 的幂(8→16)
最容易被忽略的是:分区数合规性无法靠事后观察验证——等你在 dba_tab_partitions 里看到大小不均,数据已经歪了。建表那一刻,PARTITIONS 后面那个数字就决定了后续所有 IO 均衡能否成立。











