分区数量增加本身不会直接导致查询变慢,真正拖慢查询的是优化器裁剪决策开销、元数据扫描膨胀及局部索引维护粒度与查询模式错配;执行计划出现partition range all即表明未触发分区裁剪,常见原因包括where条件未直写分区键、对其加函数或绑定变量未启用acs、局部索引未对齐查询条件、统计信息未按granularity=>'all'更新。
分区数量增加本身不会直接导致查询变慢,真正拖慢查询的是优化器在大量分区中做裁剪决策的开销、元数据扫描膨胀,以及局部索引维护粒度与查询模式错配——尤其当一次查询需跨 10+ 个分区时,i/o 和索引段访问成本会非线性上升。
执行计划里出现 PARTITION RANGE ALL 就说明没裁剪
这是最典型的信号:哪怕表有 100 个分区,只要 WHERE 条件没精确匹配分区键(比如用 WHERE user_id = 'U123' 去查按 log_time 分区的表),优化器就无法排除任何分区,只能扫全表。此时 PARTITION RANGE ALL 会出现在执行计划里,table access full 伴随高 direct path read 等待,CPU 却不高。
- 检查方式:先跑
EXPLAIN PLAN FOR SELECT ...,再查PLAN_TABLE,重点看partition_start和partition_stop列是否为KEY或具体分区名 - 常见诱因:
WHERE trunc(log_time) = date'2025-04-01'这类对分区键加函数的操作,会让裁剪失效 - 绑定变量场景下,若未启用自适应游标共享(ACS),硬解析时优化器可能因变量值不可知而退化为全分区扫描
局部索引太多反而加重 I/O 压力
每个分区对应一个独立索引段,查询跨多个分区时,Oracle 要分别访问这些段——不是“一个索引扫描”,而是“N 次小索引扫描”。当分区数从 12 增到 60,月末批量查询要访问 20+ 分区,I/O 等待就会明显飙升,性能下降 50% 不罕见。
- 验证方法:查
v$segment_statistics中对应索引段的physical reads是否随分区数增长而分散升高 - 局部索引必须和分区键强对齐;如果查询常带
bus_id但索引建在(log_time, bus_id),而条件只写bus_id = ?,前导列缺失,索引利用率极低 - 新增分区后,LOCAL 索引对应分区段状态可能为
UNUSABLE,查dba_ind_partitions可确认
统计信息没同步更新,优化器瞎猜
分区表的统计信息必须按分区粒度刷新,否则优化器基于过时或粗粒度数据估算行数,容易选错执行路径——比如该走索引却选了全表扫描,或低估跨分区数量导致并行度分配失当。
- 收集命令必须指定
granularity => 'ALL':DBMS_STATS.GATHER_TABLE_STATS(ownname=>'SCHEMA', tabname=>'T_LOG', granularity=>'ALL') - 单纯执行
DBMS_STATS.GATHER_TABLE_STATS默认只更新表级统计,各分区及 LOCAL 索引统计仍陈旧 - 新增大批量数据后未重收集,
dba_tab_partitions中的num_rows可能严重偏离实际,触发错误的 join 顺序或驱动表选择
真正卡点不在分区数量本身,而在分区键是否覆盖高频查询条件、局部索引是否被实际用上、统计信息是否真实反映每个分区的数据分布——这三个环节任意一个断掉,分区越多,性能越容易雪崩。











