PARTITION RANGE ALL 表示全分区扫描,说明分区裁剪失效;常见原因包括JOIN条件未含分区键、分区键参与函数或表达式、绑定变量值不可见、统计信息未按granularity=>'ALL'收集、局部索引未被有效利用等。
执行计划里出现 PARTITION RANGE ALL 就说明没裁剪
复合分区(比如 range-list 或 range-hash)本身不导致性能差,问题出在关联查询时优化器根本没触发分区裁剪。只要执行计划中 partition range all 出现,就代表所有子分区都被扫描——哪怕只查最近一天的数据,也可能扫几十个分区。
常见诱因包括:
- JOIN 条件里没包含任何分区键,比如主表按
log_dateRANGE 分区,但关联条件是ON a.user_id = b.user_id,优化器无法排除任意分区 - 分区键参与了表达式或函数,例如
WHERE trunc(a.log_date) = date'2026-06-14',即使log_date是第一级分区键,也会失效 - 绑定变量值在硬解析时不可知范围,尤其在复合分区中,二级分区键(如 LIST 中的
region_code)若用绑定变量且未启用自适应游标共享(ACS),裁剪常退化为全扫
局部索引在跨分区 JOIN 时容易失效
复合分区表上建 LOCAL 索引很常见,但它只对单一分区有效。当关联查询涉及多个分区(比如 LEFT JOIN 后需回表取不同分区的数据),Oracle 可能放弃使用局部索引,转而走全表扫描或全局索引(如果存在且状态正常)。
验证方式:
- 查
dba_ind_partitions,确认每个子分区的索引段status是USABLE,不是UNUSABLE - 执行计划中若出现
INDEX FULL SCAN或TABLE ACCESS FULL而非INDEX RANGE SCAN,基本可判定局部索引未被有效利用 - 复合索引字段顺序不匹配查询模式:例如建了
(log_date, region_code, user_id)的 LOCAL 索引,但 JOIN 条件只用user_id,前导列未过滤,索引大概率跳过
统计信息未按 granulariy => 'ALL' 收集
复合分区的统计信息必须逐子分区收集,否则优化器估算行数严重失真,可能误判 JOIN 方式(比如该选 HASH JOIN 却选了 NESTED LOOPS),甚至放弃分区裁剪逻辑。
正确做法:
- 运行
DBMS_STATS.GATHER_TABLE_STATS时,显式指定granularity => 'ALL',不能只用默认值 - 检查
dba_tab_statistics中global_stats和partition_stats是否都为YES - 新增子分区后,务必重新收集统计信息;仅对新分区 gather 不足以让优化器掌握整体分布
全局索引维护成本高且易失效
有人试图用 GLOBAL 索引来绕过局部索引的限制,但这是双刃剑:
- 每次
ALTER TABLE ... DROP/EXCHANGE/TRUNCATE PARTITION都会使 GLOBAL 索引失效,必须加UPDATE GLOBAL INDEXES,否则 DDL 会卡住或索引变UNUSABLE - GLOBAL 索引不参与分区裁剪,它本身是单一大结构,查询仍要遍历全部索引块,I/O 并不比全表扫描省多少
- 若建的是 GLOBAL 非唯一索引,且查询条件未覆盖其前导列,同样会退化为全索引扫描
真正难处理的是复合分区下“多键联合裁剪”的隐含依赖——优化器需要同时确认一级和二级分区键的过滤条件都满足可推导性,稍有偏差(比如一个用字面量、另一个用绑定变量),裁剪就崩了。这点在执行计划里不会直接报错,只会安静地扫全表。











