数据库搜索引擎全文检索向量数据库后端【免费下载链接】paradedbOne Postgres for your application data, full-text search, vector retrieval, and aggregations. Home of the pg_search extension.项目地址https://gitcode.com/gh_mirrors/pa/paradedb点击查看免费下载本篇技术指南聚焦 ParadeDB 官方基准套件中的一个典型查询族——join_aggregate_groupby在 Stack Overflow 数据集上将stackoverflow_posts与comments两表做等值联接再按低基数字段post_type_id做GROUP BY分组聚合COUNT、SUM并按聚合指标排序。文章会完整还原该查询的两种 SQL 变体、底层触发的 AggregateScan/JoinScan 自定义扫描管线、custom_scan_tlist等实现细节以及基准套件为何不对比原生 PostgreSQL 聚合的取舍逻辑读完你将掌握如何在 ParadeDB 上构造、理解并复现这类全文过滤 JOIN 分组聚合的分析型负载。查询场景概览stackoverflow_posts × comments 分组聚合该查询族的元信息记录在 benchmarks/datasets/stackoverflow/queries/join_aggregate_groupby/README.md 中核心描述如下联接关系stackoverflow_posts → comments一对多聚合形态带GROUP BY的分组聚合COUNT(*)与SUM(c.score)分组维度post_type_id是低基数字段排序按子聚合指标SUM(c.score)降序排列负载目标验证 DataFusion 后端的分组聚合管线grouped aggregate pipeline包括以scanrelid 0呈现时custom_scan_tlist的处理路径。表结构与联接键依据 create_tables.sql两表的关键列如下stackoverflow_postsid INTEGER PRIMARY KEY、body TEXT、post_type_id SMALLINT问题/回答等类型标识等commentsid INTEGER PRIMARY KEY、post_id INTEGER、score INTEGER、text TEXT等。联接条件p.id c.post_id正是由 config.toml 中声明的父子关系定义的comments以stackoverflow_posts为父表parent_join_col id、join_col post_id。该配置同时被load-heap等命令用于按外键拓扑加载采样数据也被基准运行用于解析查询模板。统计前提README 给出的查询特征基于 100k 数据集采样更大数据集数值可能不同code全文过滤条件在stackoverflow_posts.body上的选择性约为 75%。也就是说WHERE p.body ||| code会命中约四分之三的帖子行这是一个高选择性过滤场景被过滤后仍有大量行进入联接与聚合因此对执行器的分组、排序、物化路径都是显著压力测试。两种 SQL 变体hash_partitioned 与 range_partitioned该查询族目录下有两个可执行变体均以内联 SET 查询语句的单行复合形式存放这也是整个基准套件的通用文件约定由 benchmarks/src/main.rs 中的split_operating_point负责按分号切分SET语句与查询本身。变体一DataFusion 聚合扫描hash_partitioned文件hash_partitioned.sql-- DataFusion aggregate scan SET work_mem TO 8GB; SET paradedb.enable_aggregate_custom_scan TO on; SELECT p.post_type_id, COUNT(*), SUM(c.score) FROM stackoverflow_posts p JOIN comments c ON p.id c.post_id WHERE p.body ||| code GROUP BY p.post_type_id ORDER BY SUM(c.score) DESC;要点拆解SET work_mem TO 8GB为执行阶段提供充足的工作内存避免分组聚合/排序溢出到磁盘从而测出干净的执行延迟SET paradedb.enable_aggregate_custom_scan TO on显式开启 ParadeDB 的聚合自定义扫描即用列式聚合替换行式聚合见下节 GUC 定义|||是 ParadeDB 的全文搜索操作符这里对p.body施加code关键词过滤GROUP BY p.post_type_idORDER BY SUM(c.score) DESC完整覆盖了分组 聚合 按聚合结果排序三段式负载。变体二range 分区联接range_partitioned文件range_partitioned.sql-- DataFusion aggregate scan with range partitioned join SET work_mem TO 8GB; SET paradedb.enable_aggregate_custom_scan TO on; SET paradedb.enable_range_partitioned_join TO on; SELECT p.post_type_id, COUNT(*), SUM(c.score) FROM stackoverflow_posts p JOIN comments c ON p.id c.post_id WHERE p.body ||| code GROUP BY p.post_type_id ORDER BY SUM(c.score) DESC;与变体一的唯一差异是多了一条SET paradedb.enable_range_partitioned_join TO on它把内联接切换为**范围协同分区range co-partitioned**执行模式让联接两侧按预先记录的切分点split points协同分区避免在分布式执行时广播或重洗最大的数据侧。当同一查询目录下存在多个.sql变体时基准套件会把它们一起执行并按{query} - {variant}命名约定成组呈现见 benchmarks/README.md 的查询组织说明因此这两个变体的延迟会被并列比较直接量化range co-partitioned join相对普通哈希联接的收益。底层原理ParadeDB Aggregate Scan 与 DataFusion 分组聚合管线该查询的核心卖点是聚合本身由DataFusion 后端执行而非 PostgreSQL 原生行式聚合。实现集中在 pg_search/src/postgres/customscan/aggregatescan/mod.rs。激活路径上平面钩子与 GROUP BYAggregateScan自定义扫描名ParadeDB Aggregate Scan通过CreateUpperPathsHook参与规划。从源码可见create_custom_path只对两个阶段开放UPPERREL_GROUP_AGGGROUP BY / 聚合UPPERREL_DISTINCTSELECT DISTINCT语义等价于去重分组。该查询正属于UPPERREL_GROUP_AGG阶段。当查询含pdb.agg()聚合或paradedb.enable_aggregate_custom_scan开启时聚合路径会交给 DataFusion规划器还会校验分组语义validate_grouping_pushdown无法保证 PostgreSQL GROUP BY 语义时给出 planner warning 或报错。从代码注释看GROUPING SETS、不确定排序规则等场景会被明确拒绝。custom_scan_tlist 与 scanrelid 0README 特别点出本查询包括 custom_scan_tlist for scanrelid0这一细节。含义是聚合节点位于上平面关系upper relation其自定义扫描描述的是一棵没有基表 scanrelid 的虚拟关系因此需要通过custom_scan_tlist自定义目标列列表声明输出列执行期ExecInitCustomScan会依据custom_scan_tlist构造元组描述符谓词与投影都针对该裸元组求值。这正是聚合自定义扫描与普通基表自定义扫描BaseScan有真实 scanrelid在接口层面的关键差异也是 DataFusion 聚合管线能无缝嵌入 PostgreSQL 执行框架的机制之一。为什么这个查询一定会走 DataFusion 聚合源码中聚合后端路由规则create_custom_path内的use_datafusion判定可解释本查询的选路查询含ORDER BY聚合表达式ORDER BY SUM(c.score)代码中has_aggregate_orderby_with_limit判定会把这类查询路由到 DataFusion因为 DataFusion 原生支持SortExec(fetchK)形式的 Top-K 排序而 Tantivy 的桶式聚合对按聚合值排序没有等价能力即便没有 LIMIT带ORDER BY聚合的 GROUP BY 也已超出 Tantivy 聚合的适用范围直接落到 DataFusion 分组聚合管线。此外本查询的聚合只是普通COUNT(*)/SUM(c.score)并非pdb.agg()规格所以has_paradedb_agg为假走的是标准 GROUP BY 到 DataFusion 的通用路径GroupByClause/GroupingColumn定义于 aggregatescan/groupby.rs 所引用的模块负责从查询树中提取分组列。相关 GUC 定义两个关键开关在 pg_search/src/gucs.rs 中注册均为Userset上下文会话级可设GUC默认值语义paradedb.enable_aggregate_custom_scanon开启自定义聚合扫描在有益处时以列式聚合替换行式聚合查询变体中显式置为on以保证确定性paradedb.enable_range_partitioned_joinfalse开启范围协同分区联接开启后 DataFusion 优化器规则依据分区构建时记录的切分点对内联接进行 co-partition要求两侧表都在联接键上定义partition_by且空建索引需 reindex 后才记录切分点联接执行晚物化与范围协同分区本查询的联接侧同样由 ParadeDB 接管。JoinScan 的整体策略记录在 pg_search/src/postgres/customscan/joinscan/README.md晚物化late materialization联接、聚合、排序都在列式索引数据上完成只有最终结果行才回 PostgreSQL 堆表取元组对range_partitioned变体RangePartitioningRule会采样联接两侧并合并采样结果注入两边的PgSearchTableProvider使扫描按Partitioning::Range声明输出随后RangeCoPartitionedJoinRule在两侧分区布局兼容时把CollectLeft哈希联接翻转为Partitioned模式任务本地完成配对避免广播构建侧。相关规则实现见 range_partitioning_rule.rs。为什么该查询具备 range co-partition 的资格从 indexes/bm25.sql 可见两个 bm25 索引恰好满足enable_range_partitioned_join的前置条件——两侧都在联接键上声明了partition_bystackoverflow_posts_idxpartition_by id,owner_user_id含联接键idcomments_idxpartition_by post_id正是联接键。因此p.id c.post_id两侧天然按同一键分区开启 GUC 后即可享受协同分区执行。这与 GUC 注释中Both tables must define partition_by on the join key的要求完全吻合也是该查询族专门提供一个range_partitioned变体的原因。对比策略为何省略原生 PostgreSQL 聚合基线README 的 Comparison Strategy 部分给出了明确的取舍同样见于 benchmarks/datasets/stackoverflow/README.md原生 PostgreSQL 聚合不参与对比基于 GIN 全文索引的聚合需要扫描完整 posting list 与堆元组在大数据集上耗时数十秒到分钟级慢到无法作为可用基线unworkably slowParadeDB 即使退化为列式基表扫描也更快README 明确说明即便关闭聚合自定义扫描paradedb.enable_aggregate_custom_scan off仅靠列式 BaseScan 执行本查询ParadeDB 也快于 PostgreSQL冗余变体被裁剪为此场景准备的basescan.sql变体已在套件中删除以保持基准执行时长短套件只在代表性查询如count_filter、aggregate_topk_count、join_aggregate_count、cardinality/basescan_distinct.sql中保留基表扫描对照其余聚合查询统一直接测 DataFusion 推下aggregate_scan.sql。因此本查询族最终只保留两个高价值的 DataFusion 变体它们共同构成同一聚合查询在默认哈希联接与 range 协同分区联接下的执行差异的对照实验而非与 PostgreSQL 的横向对比。复现运行在本地跑通该查询整个套件的运行入口是 benchmarks/README.md 描述的cargo命令前置条件安装并构建了pg_search的 Postgres 实例pg_stat_statements已配置用于计时。按序执行POSTGRES_URLpostgresql://localhost:28818/postgres # 1. 加载已采样的 100k Stack Overflow 堆数据含 stackoverflow_posts 与 comments 等表 cargo run --release -- load-heap \ --url ${POSTGRES_URL} \ --dataset stackoverflow \ --size 100k # 2. 构建 bm25 索引并运行全部查询含 join_aggregate_groupby 的两个变体 cargo run --release -- benchmark \ --url ${POSTGRES_URL} \ --dataset stackoverflow \ --index bm25关键参数--index bm25选择datasets/stackoverflow/indexes/bm25.sql作为索引定义含partition_by配置与配套 B-tree/GIN 基线索引--size 100k仅影响依赖数据规模的模板参数解析聚合类查询本身不含{{ }}模板引用变体文件中的内联SET由 harness 自动切分并在执行查询前应用到会话无需手工剥离。运行后join_aggregate_groupby - hash_partitioned与join_aggregate_groupby - range_partitioned两条结果行会并列出现在 Markdown/CSV/JSON 输出中可直接对比两者延迟验证 range co-partitioned join 在本负载上的效果。小结join_aggregate_groupby是理解 ParadeDB 分析型执行路径的最小完整样例一条 SQL 同时覆盖全文过滤|||→ 等值 JOINid post_id→ GROUP BY 低基数字段 → 按聚合值排序四个阶段前三个阶段全部落到 DataFusion 列式管线AggregateScan JoinScan以custom_scan_tlist支撑scanrelid 0的虚拟关系并以可选的enable_range_partitioned_join进一步把联接升级为协同分区执行。配合基准套件省略不可行的 PostgreSQL GIN 聚合基线的对比策略该查询族既是一份可复现的延迟对照实验也是阅读 aggregatescan/mod.rs 与 joinscan/README.md 源码时最合适的实践入口。赞分享数据库搜索引擎全文检索向量数据库后端【免费下载链接】paradedbOne Postgres for your application data, full-text search, vector retrieval, and aggregations. Home of the pg_search extension.项目地址https://gitcode.com/gh_mirrors/pa/paradedb点击查看免费下载相关推荐VoiceStudio 的 Supertonic-3 引擎31 语言 CPU 端 ONNX 语音合成接入与 sidecar 隔离架构实战VoiceStudio 的 Supertonic 3 引擎31 语言 CPU 端 ONNX 语音合成接入与 sidecar 隔离架构实战 Supertonic数据库搜索引擎全文检索向量数据库后端Super Productivity 同步服务 Prisma 迁移写作规范与故障自愈实战Super Productivity 同步服务 Prisma 迁移写作规范与故障自愈实战 本文围绕 packages/super sync server/pri数据库搜索引擎全文检索向量数据库后端Bitwarden 服务器端策略强制框架PolicyRequirements 的设计与实现Bitwarden 服务器端策略强制框架PolicyRequirements 的设计与实现 导读 本指南以 Bitwarden server 仓库中 Poli数据库搜索引擎全文检索向量数据库后端上一篇HubPress故障排除大全10个常见问题及解决方案下一篇SVGR代码质量工具链示例提升SVG组件质量创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考