行业资讯

跨分区UPDATE如何优雅实现?pg_pathman的PartitionFilter、PartitionRouter与PartitionOverseer三节点原理全解析

发布时间:2026/8/22 14:18:55
跨分区UPDATE如何优雅实现?pg_pathman的PartitionFilter、PartitionRouter与PartitionOverseer三节点原理全解析 跨分区UPDATE如何优雅实现pg_pathman的PartitionFilter、PartitionRouter与PartitionOverseer三节点原理全解析【免费下载链接】pg_pathmanPartitioning tool for PostgreSQL项目地址: https://gitcode.com/gh_mirrors/pg/pg_pathmanpg_pathman 是 PostgreSQL 的一款高性能分区表管理扩展Partitioning tool for PostgreSQL支持 RANGE 与 HASH 两种分区方案。它的最大亮点之一就是用三个自定义计划节点——PartitionRouter、PartitionFilter、PartitionOverseer——优雅地解决了困扰传统触发器方案的跨分区 UPDATE难题当 UPDATE 修改了分区键、导致行必须迁移到其他分区时pg_pathman 能自动完成删除 重插入全程对 SQL 完全透明。为什么跨分区 UPDATE 是分区表的老大难在继承式分区pg_pathman 基于表继承实现分区中分区表只是一个逻辑视图数据实际存放在各个子表里。普通的 UPDATE 有一个隐含假设更新前后行所在的分区不变。可一旦执行类似这样的语句假设就被打破了UPDATE orders SET month_id 5 WHERE id 42; -- month_id 是分区键行本在orders_4更新后却应该属于orders_5。传统做法是写触发器判断并手动DELETE INSERT代码繁琐、容易出错还绕不过RETURNING、ctid、EvalPlanQual 等执行细节。pg_pathman 的思路完全不同在计划树里插入三个 CustomScan 代理节点让行迁移变成执行器内的透明动作。一张执行计划看懂三节点协作开启跨分区 UPDATE 后执行一条 UPDATEEXPLAIN输出如下摘自 README.md 官方文档Custom Scan (PartitionOverseer) - Update on partitioned_table_2 - Custom Scan (PartitionFilter) - Custom Scan (PartitionRouter) - Seq Scan on partitioned_table_2 Filter: (value 2)从下往上看三个节点各司其职节点位置职责源码文件PartitionRouter扫描层之上逐行判断更新后的行留还是走partition_router.cPartitionFilterUPDATE 与 Router 之间把需要迁移的行精准投递到目标分区partition_filter.cPartitionOverseer计划树最顶层全局调度必要时重启整个 UPDATEpartition_overseer.c计划树的组装逻辑集中在 planner_tree_modification.c扩展在查询改写阶段检测到UPDATE 可能触及分区键时就会自动把这套节点结构嵌入计划。PartitionRouter逐行判断留还是走PartitionRouter 是最底层的哨兵节点直接架在分区扫描之上。它的核心逻辑写在 partition_router.h 中ExprState *constraint; /* should tuple remain in partition? */每产出一行更新后的元组Router 就用目标分区的约束条件重新求值约束成立→ 行还属于当前分区原路放行走普通 UPDATE性能几乎不受影响约束不成立→ 行要搬家Router 会记下原行的ctid定位源行用于删除把新行向上抛给 PartitionFilter。值得一提的是Router 内部还自带了EPQStateEvalPlanQual 机制保证并发场景下删除旧行前锁等待与行版本校验的正确性——这是触发器方案很难优雅处理的部分。PartitionFilter把行精准投递到目标分区PartitionFilter 本身是 pg_pathman 的INSERT 路由专家替代 INSERT 触发器在跨分区 UPDATE 中它被复用为迁移搬运工。它的工作流程见 partition_filter.h取出行中新分区键的值调用find_partitions_for_value()定位目标分区从ResultPartsStorage缓存中取出该分区预构建好的ResultRelInfo与元组转换映射TupleConversionMap避免每行都重复打开关系、重复做列转换把转换后的元组重定向写入目标分区。由于 INSERT 路径早已深度复用该节点跨分区 UPDATE 的插入半边天然继承了自动建分区RANGE 分区、FDW 分区支持等全部能力零额外成本。PartitionOverseer整个 UPDATE 的总指挥PartitionOverseer 是三个节点中最霸道的一个头文件 partition_overseer.h 里的一句注释道出了它的本质Restart ModifyTable for unobvious reasons它包裹住整个UpdateModifyTable节点负责全局协调统一事务语义跨分区 UPDATE 实际是DELETE INSERT两步Overseer 保证二者要么都成功、要么都回滚对外表现为一条原子 UPDATE协调删除-重插入顺序先由 Router/UPDATE 完成源分区的删除再把迁移行交给 Filter 写入目标分区必要时重启下游执行以保证ctid语义正确兜底与监控在异常路径如目标分区不存在且自动建分区被禁用时给出清晰报错例如 partition_filter.h 中定义的no suitable partition for key %s。可以说Router 管判断、Filter 管搬运、Overseer 管秩序三者组合才构成完整的跨分区 UPDATE 能力。如何开启一个 GUC 参数即可由于跨分区 UPDATE 会对普通 UPDATE 增加少量判断开销每行多一次约束求值该功能默认关闭。想体验只需打开开关SET pg_pathman.enable_partitionrouter ON;完整的 GUC 清单见 README.md 的 Disabling pg_pathman 一节相关开关包括pg_pathman.enable总开关、pg_pathman.enable_partitionfilterINSERT 路由等。想自己动手验证可以直接跑仓库里的回归测试脚本 pathman_rebuild_updates.sql其中连续执行了多次跨分区 UPDATEval在 105 → 106 → 115 → 95 之间来回迁移并通过RETURNING tableoid::REGCLASS逐行验证了行最终落在了正确的分区——预期结果保存在 expected 目录 中供对照。最佳实践与注意事项按需开启如果业务中极少修改分区键建议保持默认关闭让普通 UPDATE 走最快的原生路径仅在需要跨分区迁移的会话中SET ON。配合 RANGE 自动建分区迁移目标分区若不存在RANGE 分区可自动创建HASH 分区则需提前建好全部分区否则会报no suitable partition错误。观察执行计划用EXPLAIN (COSTS OFF)确认三个节点已正确插入计划树是排查问题的第一步。留意兼容提示pg_pathman 目前支持 PostgreSQL 11~15官方建议在新建集群中评估 PostgreSQL 原生声明式分区的成熟度详见 README.md而存量继承式分区场景下这套三节点机制仍是处理跨分区 UPDATE 最优雅的方案。小结pg_pathman 用一组精心设计的 CustomScan 节点把跨分区 UPDATE 从触发器写到手软变成了一个开关、零触发器PartitionRouter—— 逐行裁决行去留兼顾并发正确性PartitionFilter—— 复用 INSERT 路由能力精准搬运迁移行PartitionOverseer—— 顶层统筹 DELETEINSERT 的事务与执行顺序。理解了这套判断 → 搬运 → 统筹的协作模式你不仅掌握了 pg_pathman 的核心机制也为理解 PostgreSQL 自定义计划节点CustomScan API这一高级扩展点打下了坚实基础。【免费下载链接】pg_pathmanPartitioning tool for PostgreSQL项目地址: https://gitcode.com/gh_mirrors/pg/pg_pathman创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考