SQL 数据归档的 PARTITION detach 与 attach 的零停机流程

1次阅读

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

SQL 数据归档的 PARTITION detach 与 attach 的零停机流程

detach 之前必须确认分区键值范围和目标表结构一致

直接 DETACH PARTITION 不会报错,但后续 ATTACH 失败往往是因为源分区和目标归档表的列定义、约束、索引甚至排序规则不一致。postgresql 要求 ATTACH 的表必须与分区表完全兼容——不是“看起来一样”,而是 d+ 输出里所有属性(包括 storagecollationnot 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+ 才支持,且只适用于 RANGELIST 分区(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 干扰

DETACHEXCHANGE 本身很快,但之后如果主表正在被大量写入,旧分区可能残留 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 rowERROR: 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 流程

事情说清了就结束。最麻烦的从来不是命令敲不对,而是 detach 后忘了归档表的 autovacuum 设置,或者 exchange 前没验证分区键值是否真在范围内——这两处一漏,半夜告警基本就来了。

text=ZqhQzanResources