pgrust 优化器原理基于代价的查询优化在 Rust 中的移植【免费下载链接】pgrustPostgres rewritten in Rust, now faster than Postgres and Clickhouse项目地址: https://gitcode.com/GitHub_Trending/pg/pgrustpgrust 优化器是用 Rust 重写 Postgres这一宏大工程中最硬核的部分它把 PostgreSQL 18.3 那套经过二十多年打磨的**基于代价的查询优化Cost-Based Optimization**逻辑一行行安全地移植到了 Rust。pgrust 目前已经能对齐超过 46,000 条 Postgres 回归测试查询而优化器正是保证输出和 Postgres 一模一样的关键枢纽。这篇文章会从零讲清楚什么是基于代价的优化、pgrust 优化器的代码长什么样、以及 Rust 移植背后的巧妙设计。什么是基于代价的查询优化新手可能好奇SQL 只是一句话为什么执行前要优化因为同一句 SQL 有无数种执行方式。例如SELECT * FROM a JOIN b ON a.idb.id可以嵌套循环连接Nest Loop外层逐行驱动内层可以哈希连接Hash Join把一边建哈希表再探测可以归并连接Merge Join两边先排序再合并还可以先做索引扫描、位图扫描、并行扫描……哪种最快取决于表有多少行、数据分布、有没有索引、内存多大。Postgres 的思路是给每种方案算一个代价分数选分数最低的。这个算分的过程就是基于代价的查询优化。而pgrust 优化器要做的就是把 Postgres 里负责算分与选路的整条流水线在 Rust 里原样复刻。pgrust 优化器代码地图 ️pgrust 的优化器完整落在crates/backend/optimizer/目录下结构与 Postgres 源码一一对应模块对应 Postgres 文件职责path/costsize/costsize.c代价模型与估算最核心path/allpaths/allpaths.c顶层路径搜索驱动path/indxpath/indxpath.c索引路径生成path/joinpath/joinrels/joinpath.c、joinrels.c连接路径与连接顺序搜索path/equivclass/、pathkeys/equivclass.c、pathkeys.c等价类与排序键推导plan/createplan/createplan.c把最优路径翻译成执行计划两个必看入口costsize 代价模型主文件整个优化器的心脏createplan 路径转计划路径到计划的分发器。代价模型核心startup_cost 与 total_cost Postgres 的代价计算用一对数字描述每个路径startup_cost启动代价输出第一行前要花的代价比如排序要先排完才能输出total_cost总代价输出完所有行总共要花的代价。估算时按扫描页数 × 页代价 处理行数 × CPU 代价累加。pgrust 在costsize/src/lib.rs中原样保留了 Postgres 的默认常量代价常量默认值含义seq_page_cost1.0顺序读一页的代价random_page_cost4.0随机读一页的代价磁盘寻道贵cpu_tuple_cost0.01处理一行元组的 CPU 代价cpu_index_tuple_cost0.005处理一行索引元组的代价cpu_operator_cost0.0025执行一次操作符的代价parallel_tuple_cost0.1并行传输一行数据的代价这些常量在 Rust 里用thread_local的Cell保存既保证了并发安全又允许运行时通过 GUC 修改——和 C 全局变量语义一致。代码里可以看到非常忠实的一对一移植pub const DEFAULT_SEQ_PAGE_COST: f64 1.0; pub const DEFAULT_RANDOM_PAGE_COST: f64 4.0;估算环节选择性Selectivity与基数估算 代价再准前提是基数估算准这条 SQL 到底会过滤出多少行pgrust 的costsize/src/sizeest.rs移植了set_baserel_size_estimates等函数用表的总行数乘以限制条件的选择性selectivity0~1 之间得到估算行数再套上clamp_row_est防止估算值越界。比如 VALUES 子句的行数直接等于列表长度函数扫描的行数取所有函数返回行数的最大值——细节都和 Postgres 完全一致。路径生成连接搜索的两种策略 当一条查询涉及多张表时连接顺序的选择是指数级的。pgrust 的path/allpaths/src/joinsearch.rs移植了make_rel_from_joinlist和standard_join_search表数量少时用动态规划逐层枚举连接组合选出全局最优表数量超过geqo_threshold默认 12时切换GEQO遗传算法近似搜索避免组合爆炸。有趣的是 pgrust 连 GEQO 也移植了相关代码在 geqo_all 中。它甚至把插件 hook 为空、核心搜索默认路径这种 Postgres 的运行时分支行为都保留了下来。每一步都能被开关控制enable_* 系列 GUC ️Postgres 允许 DBA 用SET enable_seqscanoff这类开关禁用某种执行方式。pgrust 也移植了整套机制见 costsize 的 GUC 模块扫描类enable_seqscan、enable_indexscan、enable_indexonlyscan、enable_bitmapscan连接类enable_nestloop、enable_mergejoin、enable_hashjoin聚合与并行类enable_hashagg、enable_parallel_hash、enable_partitionwise_join等每个开关都是一个thread_local布尔 Cell通过 GUC 表的访问器读写保证SET enable_hashjoinoff之后代价计算真的会把哈希连接路径罚到禁用。从最优路径到执行计划最后一公里 代价最低的路径选出来后还要把它翻译成真正的执行计划树。这一步由 createplan 模块 完成它按路径类型分发create_plan_recurse把 36 种PathNode变体逐一转换为对应的 Plan 节点——序列扫描、索引扫描、嵌套循环、聚合……一应俱全。注释里写得很清楚这是对createplan.c的1:1 移植。Rust 移植的三大工程巧思 ️1. Arena 模型没有生命周期的 PlannerInfoPostgres 里PlannerInfo是一棵充满指针的树。Rust 里指针要管生命周期pgrust 的做法是把Path、Node等都放进arena区域分配器用PathId、RelId、NodeId这类句柄代替裸指针。这样PlannerInfo本身不携带生命周期既安全又高效。2. Seam 机制让没移植完的代码也能编译整个 pgrust 是逐步移植的模块间互相依赖。它发明了seam接缝机制跨模块调用先声明一个接缝实现方没落地时先panic 占位落地后再接入。这让优化器在依赖未完成时也能整体编译、逐块点亮——注释里称之为 mirror PG and panic。3. 无 unsafe 代码allpaths模块第一行就是#![forbid(unsafe_code)]。整个优化器在Safe Rust下运行内存安全由编译器保证这是相对 C 实现最大的工程红利。为什么优化器移植值得关注pgrust 的目标不只是能用 Rust 跑 Postgres而是从内部让 Postgres 更容易被改造保持 Postgres 的行为形状、把官方回归测试当作裁判再借助 Rust 的安全性和 AI 辅助编程探索更深层的服务端改动。Roadmap 里那些大胆设想——线程化内核、内置连接池、杜绝突然变差的执行计划——全都建立在优化器这套确定性的地基之上。理解 pgrust 优化器你就理解了 Postgres 查询优化的全貌也看到了一个 C 大型系统被系统性重写为 Rust 的绝佳范本。如果你也想动手研究从costsize的代价常量开始读起是最友好的切入点。【免费下载链接】pgrustPostgres rewritten in Rust, now faster than Postgres and Clickhouse项目地址: https://gitcode.com/GitHub_Trending/pg/pgrust创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考