Oracle 11g中HASH分区的核心目的是缓解高并发DML下的索引根块争用和I/O倾斜,而非加速查询;其通过将表拆分为2的幂次方个独立分区(如16或32),使每个分区拥有独立局部索引,从而分散写入压力至多个索引根块,避免单点锁竞争。
oracle 11g 中对大表做 hash 分区,核心目的不是“加速查询”,而是缓解高并发 dml 下的索引根块(root block)争用和物理 i/o 倾斜——这是写入密集型场景(如订单号主键插入、用户行为日志)的真实痛点。
为什么 HASH 分区能缓解索引根块竞争
当大量会话同时向一张无分区的表插入数据,且该表有唯一主键索引(如 ID 自增或 UUID),所有新键值都按顺序或哈希后集中落在索引 B+ 树最右侧的叶块附近。这导致:多个会话反复争抢同一个索引根块/分支块加锁,出现大量 enq: TX - index contention 等待事件。
HASH 分区把逻辑表拆成 N 个物理段(N 必须是 2 的幂),每个分区自带独立的索引结构(局部索引)或可配全局索引。关键点在于:
- 插入时,
hash(key) & (N-1)决定数据落到哪个分区 → 同时也决定了该行对应的索引条目写入哪个分区的索引段 - 只要 N 足够大(如 16 或 32),原本集中在单个索引根块的写压力,就被分散到 N 个独立的索引根块上
- 每个分区的局部索引各自维护自己的 root/branch/leaf 结构,彼此不共享根块,彻底消除跨分区争用
PARTITIONS 数必须是 2 的幂:不只是建议,是位运算硬约束
Oracle 底层用 hash(key) & (num_partitions - 1) 替代取模,仅当 num_partitions 是 2 的幂时,该位运算才等价于均匀分布的 hash(key) % num_partitions。否则结果严重偏移:
- 设
PARTITIONS 12→ 实际计算用hash(key) & 11(二进制1011),低位比特被强制屏蔽,高位 hash 值失效 - 实测中常出现前 4 个分区占 80% 数据,后 8 个分区长期为空,I/O 和锁竞争反而更集中
-
dba_segments中可见某些分区段大小是其他分区的 5–10 倍;用select dbms_rowid.rowid_object(rowid), count(*) from t group by dbms_rowid.rowid_object(rowid)可验证对象 ID 分布极不均
局部索引 vs 全局索引:选错等于白分
HASH 分区后若建全局索引(如 CREATE INDEX idx_global ON t(id) GLOBAL),虽然仍能保证唯一性,但会直接废掉分区价值:
- 每次插入仍需访问**同一个全局索引根块**来定位插入位置,根块争用照旧
- 查询无法利用分区裁剪(partition pruning),优化器仍可能扫全索引树
- 高并发写入时,全局索引的维护开销远高于局部索引,易引发
enq: TX - row lock contention - 正确做法是建局部索引:
CREATE INDEX idx_local ON t(id) LOCAL,每个分区对应一个独立索引段,写入压力自然分散
已有大表补救:别碰 ADD PARTITION,重定义才是正解
对已上线、数据倾斜严重的非 2 的幂 HASH 表,常见错误操作是想用 ALTER TABLE ... ADD PARTITION 补足数量。但 Oracle 明确禁止:
-
ADD PARTITION仅适用于 RANGE/LIST 分区;对 HASH 分区执行必报ORA-14225: ADD PARTITION not supported for hash partitioned objects -
SPLIT PARTITION只能分裂现有分区,不能修正整体哈希分布逻辑,分裂后仍是倾斜 - 真正可行路径只有两个:
DBMS_REDEFINITION在线重定义(停机时间短,但步骤多),或CREATE TABLE AS SELECT+RENAME+ 重建索引/约束(需停机窗口)
最易被忽略的一点:哪怕你把分区数设对了,如果索引没同步改成局部(LOCAL),或者业务 SQL 查询条件不包含分区键(导致无法分区裁剪),HASH 分区带来的 I/O 和锁收益就几乎归零——它解决的是物理层争用,不是逻辑层查询加速。


















