PostgreSQL 分区表怎么设计?有什么限制和坑?
简化版
PostgreSQL 声明式分区可以把一张逻辑大表拆成多个物理分区,常见方式有范围分区、列表分区、哈希分区。它适合按时间、租户、区域等维度管理大表,让查询通过分区裁剪只扫描必要分区,也方便按分区归档和删除。但分区不是性能万能药,分区键、查询条件、索引和维护策略设计不好,反而会增加复杂度。
详细版
分区表常见场景:
- 日志、订单、流水按时间分区;
- 多租户系统按租户或租户哈希分区;
- 地域、业务线等枚举维度用列表分区;
- 超大表按哈希分散写入和查询压力。
常见坑包括:
- 查询条件不带分区键,无法有效分区裁剪;
- 分区数量过多,规划和维护成本升高;
- 忘记为每个分区建立合适索引;
- 分区键选择和业务查询模式不匹配;
- 全局唯一约束受分区键限制,需要提前设计。
完整版教学
一、分区表解决什么问题
当一张表越来越大时,可能出现:
- 查询扫描范围太大;
- 索引体积太大;
- 历史数据删除成本高;
- VACUUM、备份、归档维护困难;
- 单表冷热数据混在一起。
分区表的思路是:业务上仍然是一张表,物理上拆成多个分区。查询命中分区键时,优化器可以只访问相关分区。
二、PostgreSQL 的常见分区方式
声明式分区主要有三类:
CREATE TABLE orders (
id bigint,
created_at date not null,
amount numeric
) PARTITION BY RANGE (created_at);
范围分区适合时间:
CREATE TABLE orders_2026_07 PARTITION OF orders
FOR VALUES FROM ('2026-07-01') TO ('2026-08-01');
列表分区适合枚举值,比如地区、业务线。哈希分区适合按某个键均匀打散,比如用户 ID 或租户 ID。
三、分区裁剪是性能关键
分区表能提速的前提是查询条件能让数据库判断“只需要扫描哪些分区”。
例如按 created_at 分区,下面查询容易被裁剪:
SELECT * FROM orders
WHERE created_at >= '2026-07-01'
AND created_at < '2026-08-01';
如果查询完全不带 created_at,数据库可能仍要扫描很多分区。此时分区不仅没有明显收益,还会增加计划复杂度。
所以分区键必须和高频查询条件匹配。
四、分区表仍然需要索引
分区不是索引的替代品。分区裁剪只能帮你减少要看的分区,进入具体分区后,是否高效过滤数据仍然取决于索引。
例如订单表按月分区,但每个月仍有千万级数据,高频查询是:
SELECT * FROM orders
WHERE created_at >= '2026-07-01'
AND created_at < '2026-08-01'
AND user_id = 100;
这时每个分区上仍需要合适的 user_id 或组合索引。否则裁剪到一个月后,依然可能在月分区里扫大量数据。
五、分区数量和维护策略要可控
分区数量不是越多越好。过多分区会增加:
- 查询规划成本;
- 元数据管理成本;
- 索引维护成本;
- 自动建分区和归档逻辑复杂度。
时间分区通常要提前设计生命周期,比如自动创建未来分区、归档历史分区、删除过期分区。日志表常见做法是按天或按月分区,具体粒度取决于数据量和查询范围。
六、常见误区与追问
| 设计点 | 正确关注 | 常见问题 |
|---|---|---|
| 分区键 | 高频查询是否带上 | 不带分区键就难裁剪 |
| 分区数量 | 管理和规划成本 | 分区过多拖慢计划生成 |
| 唯一约束 | 是否包含分区键 | 全局唯一能力受限制 |
记忆钩子:分区表先问“能不能裁剪”,再问“分区内有没有索引”。分区不是索引替代品,只是先把大表切成可管理的物理块。
例如订单表按月分区,每月 3000 万行,查询 2026 年 7 月订单时只扫描 1 个分区,比扫 36 个月数据轻很多;但如果运营按 user_id=10086 查全历史订单,而 SQL 不带 created_at,数据库可能要访问 36 个分区。这个场景里,分区键和查询路径不匹配,效果就会打折。
- 误区:表大了就上分区,性能一定提升。 只有查询能做分区裁剪、维护能按分区管理时,分区收益才明显。
- 误区:分区后就不用建索引。 分区裁剪只减少分区数量,分区内部仍需要合适索引支撑过滤、排序和 JOIN。
- 误区:分区越细越好。 分区太多会增加规划、DDL、索引维护和监控成本,日分区、小时分区都要结合数据量判断。
- 追问:范围分区、列表分区、哈希分区怎么选? 时间和 ID 范围用范围分区,地区/业务线用列表分区,想均匀打散租户或用户用哈希分区。
- 追问:分区表唯一约束有什么坑? PostgreSQL 分区表上的唯一约束通常需要包含分区键,否则无法在所有分区间直接保证全局唯一。
- 追问:历史分区如何归档删除? 按分区 detach/drop 比逐行 delete 更高效,但要先确认备份、审计、外键和查询入口。
七、加强记忆
PostgreSQL 分区表的主线是:大表按业务维度拆成物理分区,查询带分区键才能裁剪,分区内仍要建索引,分区数量和生命周期要可控。分区解决的是“大表管理和扫描范围”,不是所有慢查询的通用解药。