业务表数据量增长到上亿行后,单表查询、索引维护、清理都开始变慢。分区表是 PostgreSQL 应对大数据量表的成熟方案:把一张大表按规则拆成多张物理子表,查询时可以只扫描相关分区。
一、分区类型
PostgreSQL 从 10 开始支持声明式分区,主要分三种:
| 类型 | 适用场景 | 示例 |
|---|---|---|
| RANGE 范围分区 | 按时间、按 ID 区间 | 按月份分区 |
| LIST 列表分区 | 按离散值分组 | 按地区、按状态 |
| HASH 哈希分区 | 数据无法按值划分 | 按用户 ID 散列 |
二、创建表结构
以最常见的按时间范围分区为例:
CREATE TABLE events (
id BIGSERIAL,
ts TIMESTAMPTZ NOT NULL,
payload JSONB
) PARTITION BY RANGE (ts);
CREATE TABLE events_2026_07 PARTITION OF events
FOR VALUES FROM ('2026-07-01') TO ('2026-08-01');
CREATE TABLE events_2026_08 PARTITION OF events
FOR VALUES FROM ('2026-08-01') TO ('2026-09-01');
应用层完全无感,仍然向父表 events 写入和查询,PostgreSQL 会自动把数据路由到正确的分区。
三、分区裁剪
分区表提升性能的核心是「分区裁剪」(partition pruning):查询条件里带的过滤值能锁定到少数几个分区时,优化器只扫描这些分区。
EXPLAIN SELECT * FROM events WHERE ts >= '2026-07-10' AND ts < '2026-07-12';
执行计划里可以看到 Seq Scan on events_2026_07,只会扫一个分区,而不是全表。这也是为什么分区字段必须出现在查询条件里——否则退化成全分区扫描。
四、设计原则
- 分区键要选查询高频过滤的列,否则分区裁剪用不上。
- 分区粒度要平衡:太细(如按天)分区数量爆炸,单分区太小;太粗(按年)分区裁剪收益低。业务上一般按月。
- 索引建在父表上,PG 会自动创建到所有子分区;也支持在单个分区上建局部索引。
- 避免跨分区修改:UPDATE 导致分区键变化时,PG 需要跨分区移动行,代价高。
五、历史数据维护
分区表最大的运维收益是「秒级删除历史数据」:清掉一整月的数据,只需要 DETACH 再 DROP 分区,而不是逐行 DELETE:
ALTER TABLE events DETACH PARTITION events_2026_06;
DROP TABLE events_2026_06;
相比等量的 DELETE,这种方式不产生大量 WAL 和膨胀,对在线业务影响极小。配合定时任务自动创建下月分区,就形成了完整的数据生命周期管理。
小结
分区表通过「物理拆分 + 查询裁剪」解决了大表问题,PostgreSQL 的声明式分区让这套方案几乎零维护成本。关键是选对分区键和粒度,让分区裁剪真正生效。