range分区易引发数据倾斜,因其仅按键值区间物理划分,不考虑数据实际分布;当业务写入集中(如新订单全落最新分区、活跃用户id集中在某段),会导致“1%分区存70%数据”,引发i/o、锁及并行负载失衡。
为什么 range 分区容易引发数据倾斜?
oracle 按时间或 id 范围分区时,只要业务写入集中(比如新订单全落在最新分区、用户注册 id 递增但活跃用户集中在某段),就会出现「1% 分区存 70% 数据」的情况。这不是统计信息不准导致的执行计划偏差,而是物理分布本身失衡——i/o、锁、并行任务全压在少数几个分区上。范围分区不看数据值分布,只看键值区间,天然不适合基数低或增长不均的字段。
- 典型翻车现场:
ORDER BY order_id RANGE(order_id 自增但爆款商家单日占 60% 订单) - 更隐蔽的问题:
LIST PARTITION BY status(status 只有 'PENDING'/'DONE' 两值,80% 是 PENDING) - 后果不只是慢:
V$SESSION_WAIT中大量enq: TX - row lock contention或db file sequential read集中在某几个FILE_ID/BLOCK_ID
HASH 分区能解决倾斜吗?关键看怎么选列
HASH 分区把数据散列到固定数量的分区,前提是分片键本身分布均匀。如果用 user_id 做 HASH 分区,且 user_id 是全局唯一、随机生成的 UUID 或雪花 ID,那基本能均摊;但如果用 region_code(全国只有 34 个省),哪怕设 64 个 HASH 分区,实际也只落到 34 个桶里,剩余分区空转。
- 必须验证基数:
SELECT COUNT(DISTINCT region_code) FROM orders;若结果远小于分区数(如 34 vs 64),HASH 无效 - 优先选高基数字段:
user_id、order_id、transaction_no,而非status、gender、is_deleted - 避免用函数包装:
PARTITION BY HASH(UPPER(user_name))会破坏散列均匀性,Oracle 不支持对表达式建 HASH 分区
组合分区(Composite Partitioning)怎么用才不白搭?
单一维度分区扛不住热点时,组合分区是更务实的选择。不是简单套模板,而是按访问模式拆解:高频查询常带时间条件 + 用户 ID,那就用 RANGE-LIST 或 RANGE-HASH,让时间切片控制生命周期,哈希打散内部数据。
-
RANGE-HASH示例:CREATE TABLE orders (...) PARTITION BY RANGE (order_date) SUBPARTITION BY HASH (user_id) SUBPARTITIONS 8—— 每月一个主分区,内部再分 8 个子分区,避免单月数据爆炸后无法并行 - 别滥用
LIST-HASH:若 LIST 键本身低基数(如product_type IN ('A','B','C')),子分区再 HASH 也救不了整体倾斜 - 注意 DML 影响:组合分区的
INSERT /*+ APPEND */仍可能因order_date集中写入单个主分区而触发热块争用,需配合异步归档或分区交换缓解
ALTER TABLE ... MODIFY PARTITION 之前必须确认三件事
想把现有 RANGE 表改成 HASH 或组合分区,不能直接 MODIFY PARTITION——Oracle 不支持在线转换分区类型。你实际要走的是重建路径,而最容易被忽略的是中间状态风险。
- 确认
DBMS_STATS.LOCK_TABLE_STATS是否已锁住旧表统计信息,否则重建期间新旧表统计信息混用,应用 SQL 执行计划可能突然劣化 - 检查物化视图日志:
SELECT * FROM USER_MVIEW_LOGS WHERE MASTER = 'ORDERS';若存在,CREATE TABLE AS SELECT重建后需手动重建日志,否则后续DBMS_MVIEW.REFRESH('F')会失败 - 外键依赖必须处理:
SELECT CONSTRAINT_NAME, R_CONSTRAINT_NAME FROM USER_CONSTRAINTS WHERE TABLE_NAME = 'ORDERS' AND CONSTRAINT_TYPE = 'R';重建前得先DISABLE,否则INSERT /*+ APPEND */报ORA-02298
真实调优中,最常被跳过的一步是验证分区裁剪是否生效。改完之后不查 EXPLAIN PLAN FOR SELECT ... 的 PARTITION START/STOP 字段,就以为“改了=好了”。数据还在老地方,只是名字换了。











