← 返回题目列表

PostgreSQL 分区裁剪是什么?为什么分区表不一定更快?

高频 中等 第 7 / 31 题 更新于 2026/07/29
PostgreSQL分区表分区裁剪查询优化

简化版

分区裁剪是优化器根据查询条件跳过不相关分区,只扫描可能命中的分区。分区表不一定更快,只有查询条件能命中分区键、数据规模足够大、分区数量合理、索引和维护策略正确时才有收益;否则可能增加规划和运维成本。

详细版

PostgreSQL 分区常用于按时间、租户、范围或列表拆分大表。分区裁剪依赖分区键条件,比如按 created_at 按月分区,查询某一天数据就能只扫对应月份。

常见误区:

  • 分区不是索引替代品。
  • 查询不带分区键时可能扫很多分区。
  • 分区过多会增加规划成本。
  • 每个分区的索引、约束、Vacuum 都要管理。
  • 分区键选错会导致收益很低。

分区更像数据管理和剪枝工具,不是万能性能开关。

完整版教学

一、分区表解决的是大表管理和剪枝

当一张表数据达到亿级,按时间归档、删除历史数据、局部查询都会变困难。分区表把逻辑上的一张表拆成多个物理分区。

比如订单表按月分区:

orders_2026_01
orders_2026_02
orders_2026_03

查询 2026 年 2 月订单时,如果条件包含分区键,优化器可以只访问 orders_2026_02,这就是分区裁剪。

记忆钩子:分区快不快,关键看能不能裁剪;不能裁剪时,它可能只是把一张大表拆成很多麻烦。

二、分区裁剪依赖分区键条件

如果按 created_at 分区,查询条件必须能推导出时间范围。否则优化器不知道该扫哪些分区,只能访问多个甚至全部分区。

-- 能裁剪
where created_at >= '2026-02-01'
  and created_at <  '2026-03-01'

-- 很可能不能有效裁剪
where user_id = 1001

所以分区键要贴合最常见的大范围过滤条件。日志、订单、流水常按时间分区,因为大多数查询和归档都带时间范围。

三、分区不是索引的替代品

分区减少要扫描的分区数量,索引减少单个分区内要扫描的数据量。两者解决的问题不同。

如果一个月分区里仍然有 5000 万行,查询某个用户的订单仍然需要分区内索引,比如 (user_id, created_at)

能力解决问题
分区裁剪少扫不相关分区
索引在目标分区内快速定位行
分区归档快速 detach/drop 历史数据

回答时说“分区后不用索引”会被面试官追问。

四、分区数量不是越多越好

分区太粗,裁剪效果差;分区太细,规划、维护和元数据管理成本上升。每天一个分区还是每月一个分区,要看数据量和查询粒度。

假设一天 100 万行,一月 3000 万行,如果大多数查询按天查,每日分区可能合适;如果一天只有 1 万行,每日分区就可能太碎。

分区粒度 = 数据量 + 查询范围 + 归档周期 + 运维复杂度

面试中可以强调通过数据量和查询模式决定,而不是固定按天或按月。

五、分区键会影响唯一约束和主键设计

PostgreSQL 分区表的唯一约束通常要求包含分区键,否则无法在所有分区上保证全局唯一。比如按 created_at 分区却想 unique(order_no),需要理解数据库版本和约束限制。

常见做法是业务唯一号本身全局生成,再通过唯一索引或额外表兜底;或者唯一约束包含分区键。

unique (created_at, order_no)

但这表示同一时间维度内唯一,不等价于全局订单号唯一。业务语义要讲清楚。

六、分区维护要自动化

分区表需要创建未来分区、归档历史分区、监控分区大小和索引状态。如果没有自动化,到了月初或日期边界可能插入失败。

常见维护动作:

提前创建未来 3 个月分区
定期 detach/drop 历史分区
按分区 vacuum/analyze
监控默认分区是否异常增长

默认分区可以接住异常数据,但如果默认分区一直增长,说明分区规则或数据质量有问题。

七、常见误区与追问

  • 误区:表一大就分区,分区后一定快。 查询不带分区键或分区过多时,可能更慢更复杂。
  • 误区:分区可以替代索引。 分区裁剪减少分区数量,索引负责分区内定位。
  • 误区:分区越细越好。 分区太多会增加规划和维护成本。
  • 追问:什么条件能触发分区裁剪? 查询谓词能明确限定分区键范围或取值。
  • 追问:按时间分区怎么删除历史数据? 可以 detach 或 drop 历史分区,比大批量 delete 更轻。
  • 追问:唯一索引为什么要包含分区键? 因为唯一性需要在分区布局下可验证,否则跨分区全局唯一难以直接保证。

八、面试中可以这样落地

对于订单流水表,可以按月范围分区,查询和归档都按 created_at。分区内继续建业务查询索引。

create table orders (
  id bigint,
  user_id bigint not null,
  created_at timestamp not null,
  amount numeric(18,2) not null
) partition by range (created_at);

同时说明:提前创建分区、监控默认分区、按查询设计分区内索引,历史数据通过 drop 分区归档。这个方案比单纯说“按月分区”更完整。

九、加强记忆

PostgreSQL 分区题记住“裁剪才快,分区不是索引,粒度看数据,维护要自动化”。分区的价值包括查询剪枝和历史数据管理,但查询不带分区键时收益很有限。面试时一定要讲分区键选择和分区数量边界。