pg_pathman自动建分区是怎么实现的?INSERT时免手工建表,还能后台异步创建
pg_pathman自动建分区是怎么实现的INSERT时免手工建表还能后台异步创建【免费下载链接】pg_pathmanPartitioning tool for PostgreSQL项目地址: https://gitcode.com/gh_mirrors/pg/pg_pathmanpg_pathman 是一款 PostgreSQL 分区工具它的招牌能力就是自动建分区当你执行 INSERT 时如果发现新行的分区键值超出了已有分区范围它会立刻自动创建新分区完全免手工建表更妙的是这个建表动作还能交给后台 Worker 异步执行不拖累当前事务。本文带你从源码角度看懂这套机制。为什么要自动建分区在按月分区的日志表场景里最常见的运维噩梦是跨月那天没人提前建好新分区INSERT 直接报错no partition of relation ... found业务中断。pg_pathman 的解法很激进把发现缺分区和创建分区直接缝合进 INSERT 的执行路径。你只管插数据分区表自己长。整体架构三个关键角色自动建分区涉及三个核心模块各自职责清晰角色所在文件职责PartitionFilter计划节点src/partition_filter.c拦截每一行 INSERT找到该写入哪个分区create_partitions_for_value()src/partition_creation.c发现缺分区时的决策中枢当场建 or 派后台建SpawnPartitionsWorker后台进程src/pathman_workers.c在独立事务里真正执行建分区第一步INSERT 被 PartitionFilter 节点代理pg_pathman 重写了 INSERT 的执行计划把原本的插入改成一个自定义计划节点PartitionFilter来代理。它相当于一个分诊台对每一行数据做三件事计算分区键表达式的值支持表达式分区不只是单列用二分查找在共享内存缓存的分区边界里定位目标分区如果查不到任何匹配分区nparts 0就地调用create_partitions_for_value()去创建新分区。这段查不到就建的关键逻辑就在src/partition_filter.c的select_partition_for_insert()函数里约 679~683 行。找到分区后行会被直接重定向写入目标子表父表本身可以保持空表状态。第二步决策中枢——当场建还是后台建create_partitions_for_value()位于src/partition_creation.c第 282 行是自动建分区的大脑它先从配置表pathman_config_params读取两个开关auto是否开启自动建分区对应 SQL 函数set_auto()默认开启。关闭时直接报没有分区可写入的错误spawn_using_bgw是否派后台 Worker 去建对应 SQL 函数set_set_spawn_using_bgw()默认关闭。选择后台模式还需要满足两个条件分区结构是在之前的事务中创建的即xact_object_is_visible检查通过且当前没有持有冲突锁。满足则走后台异步路径否则由当前后端进程同步完成建表保证数据一定能落盘。 为什么默认是同步因为如果表是本事务刚分区的后台进程看不到未提交的分区结构只能自己上。第三步后台异步创建的精髓——动态共享内存 独立事务这是 pg_pathman 最精巧的设计。后台路径create_partitions_for_value_bg_worker()位于src/pathman_workers.c第 314 行分四步走① 打包参数到 DSM 段PostgreSQL 的后台 Worker 启动参数只能传一个整型句柄。pg_pathman 的做法是创建一块动态共享内存DSM段把用户 OID、目标表 OID、分区键值Datum 的字节级序列化见SpawnPartitionArgs结构体定义在src/include/pathman_workers.h全部塞进去再把 DSM 句柄传给 Worker。② 拉起 SpawnPartitionsWorker主进程调用start_bgworker()启动名为SpawnPartitionsWorker的后台进程并成为锁组 Leader让 Worker 能继承锁组上下文然后阻塞等待结果。③ Worker 在独立事务中建表Worker 入口bgw_main_spawn_partitions()src/pathman_workers.c第 364 行挂载 DSM 段 → 以原 INSERT 用户的身份连接数据库 →StartTransactionCommand()开启全新的短事务→ 反序列化分区键值 → 调用create_partitions_for_value_internal()真正建表 → 提交事务 → 把新分区 Oid 写回 DSM。④ 主进程取回结果继续插入主进程醒来读取 DSM 中的新分区 Oid释放共享内存INSERT 随即写入新分区。独立事务的好处很实在建表 DDL 立刻提交生效不用等那个可能很长的 INSERT 事务结束即使 INSERT 后续回滚已建的分区也不会被卷走还能被其他会话复用。第四步按 range_interval 逐个追加分区无论同步还是后台最终都汇入create_partitions_for_value_internal()src/partition_creation.c第 361 行先对父表加ShareUpdateExclusiveLock防止并发改动分区结构加锁后二次查找——也许别的会话刚替你建好了是就直接复用并发友好的细节还没找到就从pathman_config表取出range_interval分区间隔如1 day、100调用spawn_partitions_val()从当前最大边界开始循环追加新 RANGE 分区直到目标值落进某个区间每建一个子分区都会自动复制父表的全部索引、添加 CHECK 约束并可触发用户注册的init_callback回调比如给新分区建特殊索引。⚠️ 注意只有RANGE 分区支持自动创建且range_interval不能为 NULLcreate_range_partitions()时必须指定间隔否则自动建分区直接失效。如何配置与开关-- 建表时指定 1 天间隔这是自动建分区的前提 SELECT create_range_partitions(journal, dt, 2015-01-01::date, 1 day::interval); -- 关闭某张表的自动建分区 SELECT set_auto(journal, false); -- 开启后台 Worker 异步建分区 SELECT set_set_spawn_using_bgw(journal, true);会话级还可以通过 GUC 参数pg_pathman.enable_auto_partition临时关闭整个自动建分区功能方便排障时止血。源码地图速查拦截 INSERT、查找/触发建分区select_partition_for_insert决策中枢与追加逻辑create_partitions_for_value后台 Worker 与 DSM 传参bgw_main_spawn_partitions、SpawnPartitionArgs分区与配置元数据存储pathman_config与pathman_config_params两张表结构见 README总结pg_pathman 的自动建分区 PartitionFilter 节点在 INSERT 热路径上缺了就建DSM 传参的后台 Worker 在独立短事务中异步执行 DDL基于 range_interval 的循环追加。三层设计兼顾了易用性业务代码零感知、正确性加锁 二次查找防并发冲突和性能短事务 DDL 不拖累长事务。 补充一点PostgreSQL 10 之后的原生声明式分区已经相当成熟pg_pathman 官方也建议新项目优先考虑原生方案但如果你在用 PostgreSQL 10 以下版本或需要INSERT 即自动扩分区这种原生方案不具备的能力pg_pathman 依然是经典之选。【免费下载链接】pg_pathmanPartitioning tool for PostgreSQL项目地址: https://gitcode.com/gh_mirrors/pg/pg_pathman创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考