如何为列表分区表添加默认分区_DEFAULT分区的创建与拆分限制

1次阅读

_DEFAULT 分区必须建表时定义,不可后续添加;不支持拆分,影响查询裁剪与约束继承;COPY 和 ON CONFLICT 行为存在兼容性风险。

DEFAULT 分区不能通过 ALTER TABLE ADD PARTITION 创建

postgresql 的列表分区表中,_default 分区是特殊的存在:它不能用常规的 alter table …… add partition 语句创建。你试图执行类似 alter table t add partition p_default default 会直接报错 —— 因为语法不支持,不是权限或顺序问题。

正确路径只有一条:在建表时就定义好 _DEFAULT 分区。否则后续无法补上。

  • 建表时必须显式写 FOR VALUES IN (DEFAULT)DEFAULT(取决于 PG 版本,12+ 推荐前者)
  • 如果已有分区表但没默认分区,想补上?只能重建表:导出数据 → 新建带 DEFAULT 的表结构 → 导入 → 切换名称
  • 注意:CREATE TABLE …… PARTITION OF …… DEFAULT 是合法的,但仅限于首次创建子分区时;对已存在的父表,子分区必须覆盖明确值,不能“后来居上”加默认

拆分 DEFAULT 分区会触发全量重写

一旦你有了 _DEFAULT 分区,想把它按新值拆出来(比如把一部分 status = 'archived' 数据单独切到新分区),不能用 ALTER TABLE …… SPLIT PARTITION —— PostgreSQL 根本不支持拆分 DEFAULT 分区。

替代方案是手动迁移 + 重定义约束,代价很高:

  • 先创建目标分区(如 t_archived),定义好 FOR VALUES IN ('archived')
  • INSERT INTO t_archived SELECT * FROM t_default WHERE status = 'archived' 拉数据
  • 再用 DELETE FROM t_default WHERE status = 'archived' 清理原分区(注意事务和锁)
  • 最后给 t_default 加 CHECK 约束排除已迁走的值(否则插入会违反约束)
  • 整个过程没有原子性保障,且 t_default 在迁移中持续可写,容易漏数据或冲突

DEFAULT 分区影响查询计划与约束继承

PostgreSQL 查询优化器在分区裁剪(partition pruning)时,会对 DEFAULT 分区保持“保守态度”:只要 WHERE 条件不能完全排除某值,就会把 _DEFAULT 分区纳入扫描范围。哪怕你查的是 status = 'active',而所有 active 都在明确分区里,优化器仍可能扫一遍 _DEFAULT —— 因为它的定义就是“兜底”,无法静态证明无匹配。

  • 这种行为在 EXPLAIN 中表现为多扫一个分区节点,尤其在大表 + 多条件组合时拖慢响应
  • _DEFAULT 分区上的 CHECK 约束不会自动继承父表的 NOT NULL 或其他列级约束,需手动添加,否则 INSERT 可能绕过校验
  • 如果你依赖 pg_partition_tree()pg_inherits 查分区关系,_DEFAULT 子分区的 inhparent 指向父表,但它的 pg_class.relispartition 为 true,这点和普通子分区一致,无特殊标记

DEFAULT 分区与 COPY / INSERT ON CONFLICT 的兼容性陷阱

往带 _DEFAULT 分区的表写入数据时,COPYINSERT …… ON CONFLICT 行为差异明显,容易导致静默错误或性能抖动。

  • COPY 不做分区键校验,直接按行路由;若某行值不在任何显式分区定义中,且存在 _DEFAULT 分区,就会进那里 —— 这很合理。但若没有 _DEFAULT,COPY 直接失败
  • INSERT …… ON CONFLICT 在有 _DEFAULT 时可能意外命中非预期分区:比如唯一键冲突发生在显式分区,但 UPDATE 子句更新的列涉及分区键,导致新值被重路由到 _DEFAULT,从而违反该分区的 CHECK 约束并报错 new row for relation "t_default" violates check constraint "t_default_status_check"
  • 更隐蔽的是:如果 _DEFAULT 分区上有索引缺失(比如忘了建唯一索引),ON CONFLICT 可能根本找不到冲突行,变成纯 INSERT,破坏业务逻辑

实际操作中,最常被忽略的是:DEFAULT 分区不是“懒人开关”,它是查询路径里的固定节点、数据迁移时的硬瓶颈、以及约束体系里的独立个体——它的存在本身就在改变整张表的行为边界。

text=ZqhQzanResources