pgrust:用Rust重写Postgres,已通过全部回归测试

一个大胆的工程实验
在数据库领域,PostgreSQL 是当之无愧的常青树。它起源于加州大学伯克利分校的 POSTGRES 项目(1986年),由图灵奖得主 Michael Stonebraker 主导开发,1996年引入 SQL 支持后正式更名为 PostgreSQL。值得一提的是,POSTGRES 项目本身脱胎于更早的 INGRES 项目,在其基础上引入了面向对象数据库概念和可扩展类型系统,这种"可扩展性优先"的设计哲学贯穿了 PostgreSQL 的整个演进历程。
在架构上,PostgreSQL 采用多进程模型——每个客户端连接对应一个独立的后端进程(backend process),与 MySQL 的多线程模型形成鲜明对比。这一设计源于其 Unix 哲学传统:每个连接触发 postmaster 主进程 fork 出一个独立的 backend 进程,每个进程拥有独立的内存空间(约 5-10MB 基础开销),进程崩溃不会波及其他连接。
这一 fork/exec 模型在1980年代属于系统编程主流范式,彼时操作系统级进程隔离是最可靠的故障边界。postmaster 调用 fork() 后,子进程继承父进程的内存映射(包括共享缓冲区的映射关系),但拥有完全独立的地址空间——这意味着一个后端进程因解引用野指针而崩溃,内核只会回收该进程的私有内存,其他连接的状态和共享缓冲区完全不受影响。相比之下,MySQL 的多线程模型共享同一进程的地址空间,线程崩溃可能导致整个实例不可用。
这种设计提供了更好的故障隔离性,但当并发连接数增至数千时,进程创建和上下文切换的开销会显著上升——这正是 pgBouncer 等连接池工具的价值所在。pgBouncer 支持三种池化模式:session mode(连接归还时保持后端状态,兼容性最好)、transaction mode(事务结束即归还后端连接,复用率最高,也是生产环境最常用模式)和 statement mode(每条语句后归还,但限制了事务内多语句操作,适用场景有限)。典型部署可将有效连接数从数千降至数十,从而成为高并发场景的标配。
经过近40年迭代,PostgreSQL 以严格的 ACID 事务、丰富的数据类型(JSON、数组、自定义类型)和强大的扩展机制著称,在 DB-Engines 排名中长期位居开源关系型数据库前列,被 Apple、Instagram、Spotify 等科技巨头广泛采用,成为全球最受欢迎的开源关系型数据库之一。
而近日,一个名为 pgrust 的开源项目在 GitHub 上迅速走红,单日新增 789 颗星,总星标突破 2000。它的目标听起来近乎疯狂——用 Rust 完整重写 Postgres。
更引人注目的是,该项目宣称已通过 100% 的 Postgres 回归测试(regression tests)。对于任何了解数据库开发难度的人来说,这个成绩都足以让人重新审视它的严肃性——这不是玩具级 demo,而是一次针对成熟工业级软件的系统性重构尝试。

为什么要用 Rust 重写 Postgres?
C 语言的历史包袱
Postgres 核心代码库以 C 语言编写,性能上无可置疑,但也带来了 C 语言与生俱来的隐患:手动内存管理、缓冲区溢出风险、空指针解引用等。这并非 Postgres 一家的困境——微软和谷歌的安全研究报告均指出,约70%的严重安全漏洞源于内存安全问题,美国网络安全和基础设施安全局(CISA)在2023年发布报告,明确建议新项目优先采用内存安全语言。
数据库作为承载关键业务数据的基础设施,任何内存安全漏洞都可能造成灾难性后果。PostgreSQL 的 CVE 历史中不乏此类案例,如 CVE-2018-10915 缓冲区溢出漏洞,这些问题在 Rust 的所有权模型下将从根本上被规避。
Rust 的核心优势
Rust 之所以成为系统级软件重写的热门选择,核心在于它能在编译期保证内存安全与线程安全,且无需垃圾回收器。Rust 的内存安全保证并非凭空创造——其理论根基可追溯至1990年代编程语言理论的多项研究成果:
线性类型(Linear Types) 由 Philip Wadler 于1990年提出,要求每个值恰好被使用一次,为 Rust 的"移动语义"(move semantics)奠定了基础;区域推断(Region Inference) 则源自 Cyclone 语言的研究——Cyclone 是约2001年 MIT 与 AT&T 联合研究的 C 语言安全方言,首次将"为指针标注有效生命周期范围"的思想工程化,直接影响了 Rust 的借用检查器设计。Rust 将这些理论工程化,并在2018年引入非词法作用域生命周期(Non-Lexical Lifetimes,NLL)后大幅改善了开发体验——NLL 通过数据流分析(而非原先基于词法块的静态推断)确定引用的有效范围,使借用检查器能接受更多实际上安全的代码模式,在此之前,借用检查器以"过于保守"著称,经常拒绝实际安全的代码。
Rust 的内存安全保证来自三条编译期规则:每个值有且只有一个所有者;当所有者离开作用域时值被自动释放;可以有多个不可变引用,或唯一一个可变引用,但两者不能同时存在。借用检查器(Borrow Checker)在编译阶段静态验证这些规则,内存布局和生命周期在编译完成时已完全确定。对于数据库这类高度并发的系统,Rust 还通过 Send 和 Sync trait 在类型层面区分了哪些数据结构可以安全跨线程传递——这在 C 语言中完全依赖开发者文档和代码审查来保证,极易出现隐患。这套系统的代价是较陡峭的学习曲线,但换来的是无需 GC 的内存安全保证,对数据库这类对延迟抖动极敏感的系统尤为珍贵。具体优势体现在:
- 内存安全:所有权(ownership)与借用检查器(borrow checker)从根本上杜绝了大量常见内存错误。
- 零成本抽象:提供高级语言表达力的同时,运行时性能可媲美 C/C++。
- 并发安全:编译器强制的并发规则大幅降低数据竞争风险,对高并发数据库系统尤为关键。
从 Linux 内核引入 Rust、到 Android 系统组件、Windows 驱动等基础设施软件的重写浪潮,Rust 在系统编程领域的渗透从未停歇。pgrust 正是这一趋势在数据库领域的延续。
通过 100% 回归测试意味着什么
回归测试的分量
Postgres 回归测试套件是这个项目最具说服力的地方。这套测试集位于源码 src/test/regress 目录,包含超过200个测试脚本,测试内容横跨 SQL 标准符合性(SELECT、JOIN、子查询、窗口函数)、数据类型(数值、字符串、时间、几何、JSON)、存储过程、触发器、事务隔离级别、权限系统及错误处理。
testset 框架(pg_regress)采用基于文件差异比对(diff-based)的验证方式,其严格程度值得特别说明:每个测试脚本(.sql文件)对应一个期望输出文件(.out/.expected),框架执行 SQL 后将实际输出与期望输出进行逐字节 diff 比对,连空白字符、浮点数格式、错误消息措辞都必须完全一致。这种严格性在带来高置信度的同时也造成了维护负担——平台差异(如不同 OS 的浮点行为)有时需要维护多份平台特定的期望输出文件。这套测试用例由社区数十年积累,每个新特性合并时都必须附带对应测试,是 PostgreSQL 几十年来持续扩充的核心验证防线,也是新版本发布的必要门槛。
值得注意的是,回归测试套件并不包含并发测试,PostgreSQL 社区为此维护了独立的 isolation test 框架(src/test/isolation),专门用于验证事务隔离级别的正确性,通过排列不同事务的执行步骤来复现竞态条件。pgrust 宣称通过100%回归测试,但 isolation tests 的通过情况同样值得关注。
通过 100% 回归测试,意味着 pgrust 在功能行为层面已与原版 Postgres 高度对齐——相同的 SQL 输入能产生相同的输出结果。这是远比"能跑起来"更高的标准,说明项目在协议兼容性和语义正确性上投入了大量工程努力。
需要保持的审慎
通过回归测试并不等同于可以直接投入生产。需要特别指出的是,回归测试主要验证功能正确性和输出一致性,并不覆盖并发竞态条件、极端负载下的行为或长时间运行的稳定性——这些方面需要专门的压力测试(如 pgbench)和混沌工程手段来验证。通过 100% 回归测试是必要条件,但远非充分条件。实际落地还需考量:
- 性能表现:Rust 版本在真实负载下的吞吐量与延迟能否匹敌甚至超越 C 版本,仍需大规模基准测试验证。
- 生态兼容:Postgres 拥有庞大的扩展生态(如 PostGIS、pgvector 等),这些以 C 编写的扩展能否兼容,是重写项目面临的重要挑战。
- 稳定性与运维:数十年沉淀的生产级稳定性,不是短期内可以复现的。
重写路径的技术思考
完整重写一个成熟数据库通常有两条路径:一是"渐进式",通过 FFI(Foreign Function Interface,外部函数接口)逐步替换 C 模块——这一策略的代表案例是 curl 项目对 hyper 库的引入,保留原有 C 框架逐模块替换为 Rust 实现,风险可控但 C 与 Rust 边界的 unsafe 代码仍需谨慎管理;二是"从零重建",用 Rust 重新实现整个架构,可获得最干净的架构设计,但需承担全量重新验证的成本,且须深刻理解查询规划器、执行器、WAL 与 MVCC 等数十个相互耦合的子系统。从 pgrust 通过完整回归测试的表现来看,其选择了后者,且在协议层和执行层的兼容性做得相当扎实。
值得专门说明的是 WAL(Write-Ahead Logging,预写日志)和 MVCC(Multi-Version Concurrency Control,多版本并发控制)这两个子系统的核心复杂度——而两者在 PostgreSQL 实现中的耦合程度远超直觉。
PostgreSQL 的 MVCC 实现基于堆元组(heap tuple)中直接存储的事务 ID:xmin 标记插入该版本元组的事务 ID,xmax 标记删除或更新该元组的事务 ID。读操作通过快照(snapshot)——记录了操作开始时所有活跃事务的列表——与 pg_xact(原名 clog,提交日志)中的事务状态位图共同判断某个元组版本是否对当前查询可见。这意味着 MVCC 的可见性判断不仅依赖元组自身的元数据,还依赖持久化的事务状态存储。
WAL 记录不仅包含数据变更(heap tuple 的插入/更新/删除),还包含事务状态变化(COMMIT/ABORT)。自 PostgreSQL 9.3 起引入的"全页写"(full_page_writes)机制在检查点(checkpoint)后首次修改每个页面时,会将完整页面镜像写入 WAL,以防止操作系统部分写(partial write)导致的页面损坏——这进一步加重了 WAL 与存储层的耦合,也使 WAL 日志体积显著增大。在崩溃恢复场景中,系统需要通过重放 WAL 日志重建 heap 页面,同时确保未提交事务的修改对恢复后的读操作不可见——这要求 WAL 记录中必须携带足够的 MVCC 元数据(xmin/xmax)。PostgreSQL 9.0 引入的流复制进一步加深了这种耦合:备库通过流式接收并重放 WAL 来维护与主库的数据同步,备库上的 Hot Standby 查询必须能正确处理正在重放的 WAL 与并发读操作的可见性冲突。pgrust 若要支持流复制,必须精确复现这套状态机——任何细微偏差都可能在主备切换后产生数据不一致。这两个子系统与缓冲池管理、锁管理器、事务日志深度耦合,是重写工程中最容易引入隐蔽 bug 的区域,也是 pgrust 技术可信度需要长期验证的核心战场。
尤其值得关注的是扩展生态挑战。PostgreSQL 的扩展生态是其核心竞争力之一——pgxn.org 收录超过1000个扩展,涵盖地理信息(PostGIS)、向量检索(pgvector)、时序数据(TimescaleDB)等。这些扩展通过 PostgreSQL 定义的 C 语言扩展 API 与内核深度集成,且该 API 的设计优先考虑了灵活性而非 ABI 稳定性:扩展通过包含 PostgreSQL 安装目录下的服务器头文件(server/postgres.h 等)来获得内核数据结构的定义,意味着扩展与特定 PostgreSQL 大版本深度绑定——PostgreSQL 14 编译的扩展无法直接在 PostgreSQL 15 上加载,因为内部结构体可能已改变。这与 Linux 内核明确区分稳定用户态 ABI 的策略形成鲜明对比。
API 暴露了大量内核内部结构体(如 Node、PlanState、TupleTableSlot 等)的直接内存布局——扩展代码通过指针直接访问内核数据结构,而非通过稳定的 ABI 边界。扩展还通过 PostgreSQL 的 fmgr(Function Manager)框架注册函数,并依赖 _PG_init() 入口函数在加载时向内核注册 GUC 参数(Grand Unified Configuration,PostgreSQL 的全局配置系统)和钩子函数(hooks)。
pgrust 若要在 Rust 核心上加载现有 C 扩展(如 .so/.dylib 动态库),需要提供一个 C 兼容的符号表,精确暴露 PostgreSQL 原版的函数指针和全局变量(如 CurrentMemoryContext、MyDatabaseId 等),并确保传递给扩展的结构体指针指向布局完全兼容的内存区域。这本质上要求 pgrust 的内存布局在二进制层面与原版 PostgreSQL 一致,而 Rust 编译器默认会自由重排结构体字段以优化对齐——需要大量 #[repr(C)] 标注和精心设计的 FFI 边界,本质上是在 Rust 核心外包裹一层 C ABI 仿真层。另一条路径是设计原生的 Rust 扩展接口,但这意味着整个扩展生态需要重写,短期内难以实现。PostGIS、pgvector、TimescaleDB 等核心扩展的兼容性,将直接决定 pgrust 能否在真实生产场景中形成竞争力,这是项目走向生产的最大生态障碍。
对社区而言,这类项目的真正价值或许不在于"取代 Postgres",而在于探索几个关键命题:Rust 能否胜任如此复杂的系统级软件工程?在保持完整兼容性的前提下,内存安全的重写能带来多少稳定性与安全性收益?这些问题的答案,对整个基础软件行业都具有参考意义。
结语:一场值得关注的实验
pgrust 目前拥有约 2000 星标、50 个 fork,仍处于相对早期阶段,但其增长势头和技术宣称已足以引发广泛关注。它既是 Rust 生态实力的一次展示,也是对"经典软件能否被现代语言重构"这一命题的有力回应。
无论最终能否走向生产可用,pgrust 都为社区提供了宝贵的工程范本:它验证了 Rust 在大规模系统软件重写中的可行性边界,揭示了内存安全语言与成熟 C 生态之间 ABI 兼容的深层挑战,也为后来者探索数据库内核现代化提供了可参考的技术路径。对于关注数据库、系统编程和 Rust 生态的开发者来说,这是一个值得持续追踪的开源项目。感兴趣的读者可前往其 GitHub 仓库,深入了解其架构设计与实现细节。
核心要点
核心要点
核心要点
相关推荐

My precious RAM:内存条被咬下主板引热议
Reddit热帖“My precious RAM”记录了一条内存条被从主板上咬下来的意外事故。本文还原事件、分析传播点,并给出硬件安装与防护的实用提醒。

深入PostgreSQL内核:一本免费开源的数据库原理指南
《The Internals of PostgreSQL》是一本免费在线书籍,系统讲解PostgreSQL的进程架构、存储布局、查询处理、MVCC并发控制和WAL日志机制,适合DBA和后端工程师深入理解数据库内核原理。

《塞尔达传说:时之笛》为何永不褪色?
《塞尔达传说:时之笛》为何历经多年依然经典?本文分析其从2D到3D的范式革命、与《旷野之息》的传承关系,以及其设计理念为何至今不过时。