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