← 返回题目列表

PostgreSQL 分区表怎么设计?有什么限制和坑?

高频 中等 第 6 / 31 题 更新于 2026/07/28
PostgreSQL分区表Partitioning大表设计

简化版

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 分区表的主线是:大表按业务维度拆成物理分区,查询带分区键才能裁剪,分区内仍要建索引,分区数量和生命周期要可控。分区解决的是“大表管理和扫描范围”,不是所有慢查询的通用解药。