内容提要
PostgreSQL 19 新增 SupportRequestSimplifyAggref 钩子,允许扩展在计划阶段改写聚合函数。以 SUM(x ORDER BY x) 为例,通过 prosupport 函数移除无意义排序,使千万行查询从 5.7 秒降至 3.7 秒。目前核心尚缺在聚合上挂载支持函数的 DDL,需手动修改系统目录。
延伸解读
优化原理:移除聚合中的冗余排序
文章以 SUM(x ORDER BY x) 为例,说明排序对求和结果无影响,但 PostgreSQL 仍会执行排序,导致千万行查询耗时 5.7 秒。通过 SupportRequestSimplifyAggref 钩子,扩展可在计划阶段移除无意义的 ORDER BY,使查询降至 3.7 秒,与直接 SUM(x) 性能一致。这展示了利用 prosupport 函数在计划期改写聚合的潜力。
实现细节:支持函数与安全检查
支持函数需处理 SupportRequestSimplifyAggref 请求,返回新的 Aggref 节点,且不得修改原节点。代码中需检查聚合类型、参数数量、DISTINCT 和 ORDER BY 等条件,并清除 ressortgroupref 标记以避免计划树不一致。文章强调生产代码需覆盖多种情况,并尽早返回“无法优化”以保持效率。
当前限制:缺少 DDL 与依赖管理
核心尚未提供在聚合上挂载支持函数的 DDL,ALTER FUNCTION 对聚合会报错。文章通过手动更新 pg_proc 和插入 pg_depend 记录来模拟,并强调必须建立 NORMAL 依赖,否则 DROP EXTENSION 会导致 sum(numeric) 的 prosupport 悬空,引发查询失败。此外,自定义 prosupport 不会随 pg_dump/restore 或 pg_upgrade 保留,需重新附加。
应用前景与社区进展
该机制允许扩展在计划阶段任意改写聚合,可用于清理动态生成的冗余查询,或针对已知精度和标度的 numeric 提供优化版 SUM。文章提到原型扩展 pg_numeric_agg_support,并指出社区正在讨论为 CREATE AGGREGATE 和 ALTER AGGREGATE 添加 SUPPORT 选项的补丁,以简化使用。
Q&A
PostgreSQL 19 新增的 SupportRequestSimplifyAggref 钩子是什么?
SupportRequestSimplifyAggref 是 PostgreSQL 19 中新增的扩展钩子,允许扩展通过 planner support functions(prosupport)在计划阶段改写聚合函数。它使扩展能够自定义聚合转换逻辑,例如移除聚合中无意义的排序。
如何通过扩展优化 SUM(x ORDER BY x) 查询?
可以编写一个 C 支持函数,在 SupportRequestSimplifyAggref 请求中检查聚合是否为 SUM 且参数类型为整数或 numeric,然后复制聚合节点并移除 aggorder 和 ressortgroupref,从而消除不必要的排序。通过手动更新 pg_proc 和 pg_depend 将支持函数附加到 sum(numeric) 上。
移除 SUM(x ORDER BY x) 中的排序能带来多少性能提升?
在千万行数据上,带排序的 SUM(x ORDER BY x) 查询耗时约 5.7 秒,移除排序后降至约 3.7 秒,节省了约三分之一的时间,与直接写 SUM(x) 的性能相同。
为什么 PostgreSQL 核心目前无法直接为聚合函数添加支持函数?
因为社区 PostgreSQL 缺少在聚合上挂载支持函数的 DDL 语句。尝试使用 ALTER FUNCTION pg_catalog.sum(numeric) SUPPORT ... 会报错,提示“pg_catalog.sum”是聚合函数。目前只能通过手动修改系统目录(pg_proc 和 pg_depend)来实现。
在手动附加支持函数时,为什么需要在 pg_depend 中记录依赖?
记录 NORMAL 依赖可以防止在删除扩展时,sum(numeric) 的 prosupport 引用变成悬空指针,导致后续查询失败。有了依赖,系统会阻止删除扩展,直到先调用 detach 函数清除引用。
自定义 prosupport 函数在 pg_dump/restore 或 pg_upgrade 后是否保留?
不会保留。自定义 prosupport 函数不会在 pg_dump/restore 或 pg_upgrade 中存活,如果新集群需要相同的优化,必须重新执行附加操作。