detach前须确保分区键范围和表结构完全一致,包括列定义、约束、索引、排序规则等;推荐用exchange partition替代detach+attach实现原子归档,并临时禁用归档表autovacuum以避免干扰。

detach 之前必须确认分区键值范围和目标表结构一致
直接 DETACH PARTITION 不会报错,但后续 ATTACH 失败往往是因为源分区和目标归档表的列定义、约束、索引甚至排序规则不一致。PostgreSQL 要求 ATTACH 的表必须与分区表完全兼容——不是“看起来一样”,而是 \d+ 输出里所有属性(包括 storage、collation、not null)都得对得上。
- 用
\d+ 表名对比源分区(如sales_2023_q1)和归档表(如sales_archive_2023_q1)的完整结构 - 归档表不能有额外索引或触发器;如果有,先删掉,
ATTACH成功后再重建(否则报ERROR: cannot attach partition with indexes) - 如果原表用了
GENERATED ALWAYS AS IDENTITY,归档表对应列也得是同种生成方式,否则ATTACH拒绝
用 EXCHANGE PARTITION 替代 detach + attach 可避免中间状态暴露
单纯 DETACH 后,数据暂时脱离分区树,查询主表时这部分数据就查不到;而 EXCHANGE PARTITION 是原子操作:它把一个普通表和分区互换身份,全程主表可查、无间隙。但注意:PostgreSQL 12+ 才支持,且只适用于 RANGE 和 LIST 分区(HASH 不行)。
- 确保归档表已按相同分区键建好,且数据满足该分区边界(例如
VALUES FROM ('2023-01-01') TO ('2023-04-01')) - 执行
ALTER TABLE sales EXCHANGE PARTITION sales_2023_q1 WITH TABLE sales_archive_2023_q1 - 交换后,
sales_archive_2023_q1变成正式分区,原归档表变成空的普通表,可立即重用
零停机的关键是绕过 VACUUM 和 AUTOVACUUM 干扰
DETACH 或 EXCHANGE 本身很快,但之后如果主表正在被大量写入,旧分区可能残留 tuple visibility 问题,导致 VACUUM 卡住或触发长事务阻塞。更隐蔽的是:归档表若开启 autovacuum,可能在 ATTACH 后立刻启动清理,干扰主表查询计划缓存。
- 操作前临时关闭归档表的自动清理:
ALTER TABLE sales_archive_2023_q1 SET (autovacuum_enabled = false) - 操作完成后,等主表负载低谷期再手动跑一次
VACUUM sales_archive_2023_q1,而不是依赖自动触发 - 检查
pg_stat_progress_vacuum,确认没有长运行 vacuum 正在影响相关表
attach 失败时别硬重试,先查 pg_partitioned_table 和 pg_inherits
常见错误像 ERROR: partition constraint is violated by some row 或 ERROR: table is not partitioned,表面是数据或结构问题,实际常因元数据残留。比如之前 detach 过但没清干净,或者用 CREATE TABLE ... PARTITION OF 手动建过子表但没走标准流程。
- 查
SELECT inhrelid::regclass FROM pg_inherits WHERE inhparent = 'sales'::regclass,确认当前有哪些子分区还挂载着 - 查
SELECT partstrat, partnatts FROM pg_partitioned_table WHERE partrelid = 'sales'::regclass,核对主表分区策略是否仍为有效状态 - 如果发现异常子表(比如名字对不上、状态残缺),用
DROP TABLE IF EXISTS sales_2023_q1清理,再重新走 exchange 流程










