分区表改完反而更慢,大概率是查询未触发分区裁剪而全表扫描:需确保SQL显式过滤分区键、统计信息最新、避免对分区键使用函数或绑定变量导致裁剪失效,执行计划中PARTITION RANGE ALL即为典型信号。
分区表改完反而更慢,大概率不是分区本身的问题,而是查询根本没走分区裁剪,还在全表扫描——direct path read 占比高、cpu 低、执行计划里出现 table access full,基本可以锁定这个方向。
为什么加了分区却还是全表扫描
分区结构存在 ≠ 查询能用上分区。Oracle 优化器是否启用分区裁剪(Partition Pruning),取决于 SQL 中是否显式、准确地过滤了分区键,且该列的统计信息必须最新。
- 查询条件没包含分区键,比如按
log_time分区,但 SQL 写的是WHERE user_id = 'U123',优化器无法排除任何分区,只能扫全表 - 分区键用了函数或表达式,例如
WHERE trunc(log_time) = date'2025-04-01',即使log_time是分区键,也会导致裁剪失效 - 使用了绑定变量但未启用自适应游标共享(ACS),或变量值在硬解析时无法确定分区范围,也可能退化为全分区扫描
- 执行计划里看到
PARTITION RANGE ALL就是典型信号:所有分区都被纳入扫描范围
索引失效的常见表现与验证方式
局部索引(LOCAL)建了不等于能用;全局索引(GLOBAL)没维护好,一样会跳过。关键看执行计划中是否出现 INDEX RANGE SCAN 或 INDEX UNIQUE SCAN,而不是 INDEX FULL SCAN 或直接回表全扫。
- 检查索引状态:
SELECT status, leaf_blocks FROM dba_indexes WHERE index_name = 'IDX_HIS_LOG_TIME';,若status不是VALID,索引不可用 - 确认索引字段顺序是否匹配查询条件:比如建了
(log_time, bus_id)的复合索引,但查询只用bus_id,很可能无法高效使用 - 局部索引未随分区新增而自动创建:手动添加新分区后,
LOCAL索引对应分区段可能缺失,查dba_ind_partitions可发现status为UNUSABLE - 统计信息陈旧:特别是新增大量数据后未收集,
DBMS_STATS.GATHER_TABLE_STATS必须指定granularity => 'ALL'才能同步更新每个分区及索引统计
执行计划里看不到分区裁剪?先做这三件事
别急着重写 SQL 或重建索引,先快速验证基础配置是否就绪。很多“性能下降”其实是元数据层面的疏漏。
- 用
EXPLAIN PLAN FOR ...后查PLAN_TABLE,重点看partition_start和partition_stop列:若显示KEY或ROW LOCATION,说明裁剪逻辑已触发;若为ALL,就是没裁剪 - 运行
SELECT partitioning_type, partition_key_columns FROM dba_part_tables WHERE table_name = 'TM_BUS_REALTIME_HIS';,确认分区类型(RANGE最常用)和键列名拼写、大小写是否与 SQL 中完全一致 - 检查当前会话是否禁用了裁剪:
SHOW PARAMETER "_optimizer_prune_enabled",非必要不建议设为FALSE;生产环境应保持默认TRUE
真正难处理的,往往是那些看似用了分区键、也走了索引,但执行计划里 partition_start 显示 KEY,实际却扫描了几十个分区——这时得盯住绑定变量的窥探行为、直方图分布、以及分区边界值是否连续无空洞。这些细节一旦错位,优化器就容易“猜错”。











