业务表数据量增长到上亿行后,单表查询、索引维护、清理都开始变慢。分区表是 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,只会扫一个分区,而不是全表。这也是为什么分区字段必须出现在查询条件里——否则退化成全分区扫描。

四、设计原则

  1. 分区键要选查询高频过滤的列,否则分区裁剪用不上。
  2. 分区粒度要平衡:太细(如按天)分区数量爆炸,单分区太小;太粗(按年)分区裁剪收益低。业务上一般按月。
  3. 索引建在父表上,PG 会自动创建到所有子分区;也支持在单个分区上建局部索引。
  4. 避免跨分区修改:UPDATE 导致分区键变化时,PG 需要跨分区移动行,代价高。

五、历史数据维护

分区表最大的运维收益是「秒级删除历史数据」:清掉一整月的数据,只需要 DETACH 再 DROP 分区,而不是逐行 DELETE:

ALTER TABLE events DETACH PARTITION events_2026_06;
DROP TABLE events_2026_06;

相比等量的 DELETE,这种方式不产生大量 WAL 和膨胀,对在线业务影响极小。配合定时任务自动创建下月分区,就形成了完整的数据生命周期管理。

小结

分区表通过「物理拆分 + 查询裁剪」解决了大表问题,PostgreSQL 的声明式分区让这套方案几乎零维护成本。关键是选对分区键和粒度,让分区裁剪真正生效。