SQL 如何用分区索引(local/global)在分区表上的选择

9次阅读

Local索引必须与分区键对齐,因其本质是“每个分区一张索引”,物理上绑定对应表分区,故建索引时必须包含且顺序一致于分区键全部列,否则无法保证查询精准下推至单一分区。

SQL 如何用分区索引(local/global)在分区表上的选择

Local 索引为什么必须和分区键对齐

Local 索引本质是“每个分区一张索引”,物理上和对应表分区一一绑定。这意味着 CREATE INDEX ... LOCAL 时,oracle(或 postgresql 分区索引等效实现)会强制要求索引键**必须包含分区键的全部列**,且顺序需一致——否则无法保证查询能精准下推到单个分区。

常见错误现象:ORA-14035: invalid partition key specified for local index 或建索引时提示“partitioning column not in index key”。

  • 若按 sale_date 范围分区,local 索引必须以 sale_date 开头(如 INDEX(sale_date, product_id)),不能只建 INDEX(product_id)
  • 即使业务常查 product_id,也要接受前导列冗余;否则只能选 global
  • 好处是分区维护(如 DROP PARTITION)时 local 索引自动失效对应片段,不锁全表

Global 索引适合高频非分区键查询但代价高

Global 索引是跨分区的单个 B-tree,支持任意列组合建索引,无需包含分区键——这正是它灵活的地方。但灵活性换来的是维护成本:任何分区 DDL(ALTER table ... DROP/EXCHANGE PARTITION)都会使整个 global 索引失效,必须显式 ALTER INDEX ... REBUILD 或加 UPDATE GLOBAL INDEXES 子句(Oracle 12c+)。

  • 典型场景:用户按 user_id 频繁查询,而表按 create_time 分区,此时 GLOBAL INDEX(user_id) 是合理选择
  • 注意 UPDATE GLOBAL INDEXES 会延长 DDL 执行时间,且重建期间索引不可用,需评估窗口期
  • PostgreSQL 中无原生 global 索引概念,需用普通 B-tree 索引 + 分区约束下推,效果类似但无自动失效机制

执行计划里怎么看索引是否真正生效

光看 EXPLaiN 显示用了某个索引不够,关键要确认是否发生分区裁剪(partition pruning)和索引范围扫描是否落到具体分区。Oracle 中重点关注 PARTITION RANGE SINGLEALL,以及 INDEX RANGE SCAN 的 object_name 是否带分区后缀(如 IX_LOG_2024Q1)。

  • Local 索引命中时,执行计划通常出现 PARTITION RANGE SINGLE + INDEX RANGE SCAN,且 OBJECT_NAME 是子分区名
  • Global 索引即使走索引,也可能显示 PARTITION RANGE ALL 或无分区信息,因为索引本身不分区
  • DBMS_XPLAN.DISPLAY_CURSOR 查真实执行统计,比 EXPLAIN PLAN 更准,尤其涉及绑定变量时

混合策略:一个表上 local 和 global 索引可以共存

没有“只能选一种”的限制。实际中常见组合:用 local 索引支撑分区键高频过滤(如按天查日志),再加 1–2 个 global 索引支撑核心业务维度(如订单号、客户 ID)。但要注意索引总数影响 DML 性能——每次 INSERT/UPDATE 都要维护所有相关索引。

  • local 索引数量 = 分区数 × 1(每个分区一份),global 索引数量 = 1(全局一份)
  • 如果分区数达上百,local 索引的元数据开销和收集统计信息耗时明显上升,DBMS_STATS.GATHER_TABLE_STATS 需指定 GRANULARITY => 'PARTITION' 分批处理
  • global 索引越多,DROP PARTITION 后重建越慢;建议把 global 索引控制在 3 个以内,优先保最关键的查询路径

真正难的不是语法怎么写,是预判未来半年的查询模式和分区变更节奏——索引一旦建错,重建成本远高于初期多花十分钟想清楚。

text=ZqhQzanResources