跳过正文
  1. AI/

AI 用 Rust 重写 PostgreSQL?别逗了

·8061 字·17 分钟· ·
冯若航
作者
冯若航
Pigsty 创始人, @Vonng
目录

前两天,一个叫 pgrust 的项目冲上了 Hacker News 头条:作者借助 AI,用 Rust 搬了一遍 PostgreSQL,还跑通了核心回归测试。 中文媒体的标题已经在路上了:“AI 用 Rust 重写了 PostgreSQL”。

可我把仓库、提交历史和测试脚本翻了一遍之后,觉得这件事最有意思的部分,恰恰不在标题里。 pgrust 花了两个月,亲手证明了一个和二手标题相反的事实:它用两个月时间,亲手证明了自己想推翻的东西。

pgrust.webp

引子:一天十二倍
#

先说作者。Michael Malis 不是跑来蹭热点的外行。他在 Heap 管过 PB 级的 Postgres 集群,写过几十篇 Postgres 内核剖析; 后来还当过 120 人公司 Freshpaint 的 CEO,甚至表演过用递归 CTE 写 Lisp 解释器。

所以这件事的正确打开方式,不是“外行拿 AI 攒了个数据库”,而是一个真正懂 PG 内核的人,拿着最新一批 AI 编码工具,真金白银做了一场实验。

7 月 9 日,项目被人转到 HN。截至截稿,帖子拿到 688 分和五百八十多条评论,项目星数则在一天里从 122 涨到 1,499。 README 上最抢眼的是三个数字:跑通 PostgreSQL 18.3 核心回归套件里的 46,066 条查询; 以及一个尚未发布的新版本在 TPC-C 上比 PG 快 50%;分析负载快约 300 倍。

老冯最近正好 token 多得花不完,就让 Claude Fable 5 和 Codex 5.6 一起帮着验了验货。结果越看越有意思。


一、你看到的已经是第二条命
#

很多报道没提,pgrust 其实已经死过一次了。现在 GitHub 上这版是第二版。

第一版从四月开始。Malis 让 Codex 从零写一个 Rust 版 Postgres:两周写出 25 万行代码,跑过了大约三分之一的回归测试。 随后他一口气开了 8 个 Codex 账号,每月花 1,600 美元,同时拉起十几到二十个 agent 并行干活。两周合并 280 个 PR,四月底做到 67%,五月初宣布已经到了 96%。势头看着相当猛。

然后到了 6 月 23 日,整套代码被归档进 archive/pre-fabled-2026-06-23 分支。截至截稿,此后再也没动过。

archive.webp

第二版其实在 6 月 12 日就另起炉灶了。这一次,方法彻底变了。作者在 HN 里亲口解释:先用 c2rust 把 PostgreSQL 的 C 源码 机械翻译 成 Rust,得到一批虽然充斥 unsafe,却已经能跑 PG 核心 SQL 回归测试的代码。 然后再把 Postgres 拆成大约一千个 crate,交给 Claude 逐个改成更地道的 Rust 写法。 流水线只有三步:挑下一个 crate、重写、审计。绝大多数 crate 都走了一遍;parser 等少数由工具生成的模块,仍保留了大量机械翻译代码和 unsafe

这次快得吓人。首个提交在 6 月 12 日,提交信息是“Bootstrap pgrust: catalog, seam architecture, and porting skills”。 到 6 月 24 日收工,总共 13 天、7,103 个 commit,最猛的一天提交了 1,419 个。

git 作者统计也很有节目效果:Michael Malis 署名 6,264 个提交,Claude Fable 5 署名 832 个,合伙人 Jason Seibel 署名 7 个。 最后产出 240 万行 Rust、3,525 个文件、1,464 个 crate。6 月 25 日发布时,项目宣布跑通 PG 18.3 核心回归 schedule 和 isolation 测试, 而且磁盘格式兼容,可以直接拿一个现成的 PostgreSQL 18.3 数据目录启动。

“直接用原版数据目录启动”听着像魔法,拆开看却很朴素: 它之所以这么像 PostgreSQL,是因为它本来就是从 PostgreSQL 的代码翻译过来的。

这不叫“重写”,这叫“洗代码”

这两条路线放在一起,至少有一件事很清楚。一个懂 PG 内核、又肯真金白银砸 AI 算力的人, 先试了两个月从零重写,没走通;换成直接翻译 PostgreSQL 本体,13 天就交了满分答卷。

请注意这条时间线说明了什么。一个手握无限 AI 算力、对 PG 内核了如指掌的专家,用两个月的真金白银回答了一个问题: 数据库这样的基础设施代码可以被 AI 凭空重造吗?答案是不能。 从零重写的一代死了,直译 PG 本体的二代活了。他最后能交出满分答卷,靠的是把 Postgres 三十年的代码沉积原封不动搬进来,连考卷都是 PG 社区出的。 这场实验最扎实的成果,就是它自己营销话术的反面

还有个小细节,能看出发布时有多赶:截至我检查时,没有在任何 .rs 文件头里看到 PostgreSQL 的版权声明; 我找到的归属说明,只在测试数据目录的一份 README 里。项目整体采用 AGPLv3。代码翻译过去了,系谱却丢失了。


二、你测试的,到底是什么?
#

先把该给的敬意给足:pgrust 的回归测试成绩是真的。

仓库直接带上了 PG 18.3 的官方测试文件。我让 Claude 抽查了 18 个文件,包括 parallel_schedulejoin.outnumeric.outselect_parallel.outplpgsql.out 和 isolation 的 schedule,再和上游 REL_18_3 标签逐字节比对,结果全部一致parallel_schedule 列出的 230 个测试项目也一个没少。

我没有发现篡改期望输出的迹象。runner 脚本也写得很干净:clone 仓库、用 Rust 工具链编出 pgrust,再配好 PostgreSQL 18 的 psql 客户端即可复现。vibe coding 项目遍地都是的今天,至少这份成绩单看不出掺水。

但成绩是真的,不等于二手转述里附送的那些结论也是真的。这里还有四件事得说清楚。

第一,公开 runner 跑的是串行测试。 它把大约 230 个 SQL 脚本拆开,一个接一个执行。真正的 pg_regress 会按 parallel group 并行执行,同时拉起二十来个会话; 公开的复现路径没有覆盖这种调度方式。而且服务端是带 -F 启动的,也就是关了 fsync。回归测试这么跑很正常,不算作弊;但它证明的是功能输出,不是并行调度下的行为,更不是持久性。

第二,截至截稿,公开仓库没有 CI。 作者跑通了一次,不等于后面的每个提交都还能跑通。满分状态没有被持续验证。

第三,isolation 测试不在开箱路径里。 想跑它,还得另备一棵 PostgreSQL 源码树。

第四,也是最容易上标题的那条:TPC-C 快 50%、分析快 300 倍,来自一个尚未发布的新版本。 截至截稿,支撑这两项性能数字的代码还没有公开,也没有 benchmark 配置和硬件说明。 能看的只有作者在 HN 里的补充:新版本除了列存,还用了批式执行、并行化和更快的哈希表;ClickBench 的成绩大约比 ClickHouse 慢 2 倍。

原生行存 PG 在 ClickBench 上,本来就比 ClickHouse 慢两三个数量级。所以“比 PG 快 300 倍,同时比 ClickHouse 慢 2 倍”, 算术上并不离谱。很多接入 DuckDB 一类分析引擎的 PG 扩展,也能在 ClickBench 上比原生 PG 快几百倍。

但这跟“Rust 比 C 快 300 倍”完全是两回事。存储布局、执行模型、并行策略都换了。 在代码和 benchmark 工件公开之前,谁也说不清这 300 倍分别从哪来。 它最多说明 PG 的分析架构有很大的改造空间,证明不了“AI 把 C 翻成 Rust,性能就凭空涨了 300 倍”。

PgCat 的作者 levkk 在评论区只问了一句:fsync 开了吗?回归测试可查不出有问题的 I/O 路径。这一句就问到了点子上。

不过话也得说回来,Malis 本人的表述其实很克制:尚未达到生产可用状态,没有做性能优化,现有扩展也不兼容,这些都白纸黑字写在 README 里。真正一路狂奔的,是二手转述。


三、Bun 恰好是个对照组
#

差不多同一时间,Bun 也把 JavaScript 运行时从 Zig 翻成了 Rust。于是有人会问:Bun 不是成功了,为什么 pgrust 不行?

在我看来,Bun 不但不是反例,反而是一个再合适不过的对照组。

先看方法。Jarred Sumner 在 7 月 8 日发的复盘里说得很清楚:迁移发生在 5 月 3 日到 14 日。 他们没有从零重写,因为那样要冻结一年的功能开发;而是先让 Claude 总结 Zig 到 Rust 的移植模式, 再把每个 .zig 文件机械地搬成 .rs 文件,先做出一个“看起来就是 Zig 代码转译版”的 Rust 实现, 发布以后再慢慢改成惯用写法。大约 50 个 Claude Code 动态工作流,跑了 11 天。

而且 Bun 本来就有翻译基因:最初的 Bun 转译器,就是从 esbuild 的 Go 代码逐行移植到 Zig 的。

巧合还不止这些。Bun 用的是 Claude Fable 5 的预发布版;pgrust 第二版里,Fable 署名了 832 个 commit。 Bun 官方复盘的口径是 6,778 个 commit、101 万行新增代码(剔除 merge 后,动画重放的是 6,502 个); pgrust 是 7,103 个 commit、240 万行。一个干了 11 天,一个干了 13 天。模型相同,方法相近,时间也撞在一起。

Bun(Zig → Rust)pgrust(C → Rust)
方法机械移植,后续惯用化c2rust 直译,逐 crate 惯用化
模型Claude Fable 5(预发布)Claude Fable 5(832 个署名提交)
工期11 天13 天
体量101 万行新增,6,778 commits240 万行,7,103 commits
验证基准自家百万断言测试套件(TypeScript 写的,语言无关)PG 核心 SQL 回归 + isolation 套件
翻译对象自己的生产系统基于上游代码的新实现
当前状态Claude Code 自 v2.1.181 起使用 Rust 版 Bun,Prisma 公开背书作者明确称尚未生产就绪,暂无公开生产案例

真正的差别在最后两行。

Bun 翻译的是自己的系统。代码换了语言,团队没换,测试没换,用户没换,商标和责任主体也没换。 翻译完直接扔进自家的生产管道里验证:Prisma 也拿真实故障场景复测过,出了问题还是原来那拨人接电话、修 bug、发版本。

pgrust 翻译的是别人的代码。它把 PostgreSQL 的代码文本搬了过来,但 PostgreSQL 背后的开发者、用户、发布流程和责任体系都还在原地。 代码可以复制,原来的社区与责任主体却不会跟过来。

即便条件这么好,Bun 也不是无痛迁移。官方承认迁移过程中出现过 19 个回归,后来全部修掉;按 API 价格估算,token 成本大约 16.5 万美元。 另外还有一场说不清的信任风波:Zig 作者 Andrew Kelley 认为所谓性能提升来自 Zig 本来就支持的 LTO,并称 Bun 团队私下承认没有做 fuzzing; Bun 官方则明确说,Zig 时代已经用 Fuzzilli 对 runtime API 做 24×7 fuzzing。两种说法的口径明显对不上。

lobste.rs 上有人给这种做法起了个很贴切的名字:vibe porting。

所以 Bun 真正证明的是:AI 大规模翻译代码这条路已经能走通,但最适合走这条路的,首先是原项目团队。 原班人马带着完整的测试、用户和生产环境,尚且要交这些学费;一个脱离原团队、用户和生产体系的 fork,只会更难。


四、回归测试到底带走了什么
#

pgrust 跑通了全部核心回归测试,当然继承了很多东西。问题是,它继承了多少?

PostgreSQL 的可靠性知识,大致住在四个地方。

第一层在测试里。 这部分已经固化成 46,066 条回归查询,可以复制,也可以重复执行。背后压着三十年的功能语义和故障修复。pgrust 确实把这张核心考卷完整搬走了。

第二层在代码里。 那些看起来多余的防御性检查、反直觉的执行顺序,以及那些指向多年以前讨论的注释,都是以前踩坑留下的痕迹。修 bug 的人可能早就走了,但修复本身还留在代码形状里。

这也解释了为什么第一版死、第二版活:从零重写很容易漏掉这些痕迹,机械翻译至少能把大部分一起带走。 当然翻译也会摔碎一些东西。按 Bun 的复盘,那 19 个回归大多就出在两种语言写法看着一样、实际语义不同的地方。

第三层在历史档案里。 当年为什么要这么改,另外几种方案为什么没有被采用,它们又提出过哪些反例, 这些因果关系散落在三十年的 pgsql-hackers 邮件和 commit message 里。只翻代码带不走它们。 更多的经验则散落在无数公司内部的运维管理手册 SOP 文档中。

第四层在人脑里。 老开发者看一个 patch,会本能地问“这里会不会踩到复制槽那个老坑”。 这种直觉既不在代码里,也不在测试里,更没法用 c2rust 翻过去。

问题就出在这里。pgrust 的目标是“让 Postgres 更容易从内部改变”。 可要把数据库跑起来,前两层知识也许够用;真要继续改内核,最需要的偏偏是后两层,而这两层正是代码翻译带不走的。

“那把测试补得足够厚,不就行了?”PG 自己的历史已经回答过这个问题。

2018 年的 fsyncgate 暴露出,PG 对 Linux fsync 错误语义的假设错了二十年,最后只能改成 “fsync 失败直接 PANIC”。 9.3 的 multixact 数据损坏,原班开发者和主要历史上下文都在手,仍然修了一年多才在小版本里收干净。 到了 2020 年,Jepsen 用一套全新的方法去测已经 25 岁的 PG 12.3 上,照样在可串行化隔离级别下找出了货真价实的 G2-item 反例,社区随后用几周修掉。

也就是说,考卷永远不会出完。PostgreSQL 真正厉害的不是手里已经有多少道题,而是有人持续发现问题、复盘原因、修代码,再把新题补进测试。 Hyrum 定律说得更绝:用户足够多以后,系统的每一种可观察行为都可能被人依赖。对 PostgreSQL 来说,真正的规格就是 PostgreSQL 自己,测试只能覆盖我们已经知道要测的东西。

这时候通常有人会举 SQLite:团队那么小,不也做得很稳?可 SQLite 付的是另一种账。 它有专有的 TH3 测试套件、100% MC/DC 覆盖,测试代码大约是库代码的六百倍,还有二十年、几十亿台设备跑出来的实战记录。 SQLite 把代码放进公有领域免费送,TH3 则作为商业测试产品向客户提供。它没有绕开验证成本,只是把大社区做的事,收进了自己的公司里。 至于靠完整的形式化验证覆盖整个通用 DBMS,目前也不现实。seL4 验证一个一万行左右的内核,就花了二十人年量级。


五、扩展是一份长期合同
#

除了测试和知识,还有一道更现实的门槛:扩展生态。

PostgreSQL 的 C 扩展接口包括 fmgr 调用约定、hooks、共享内存、server headers、PGXS,以及大量事实上可见的全局符号。 它从来没有承诺跨大版本稳定 ABI,但现实中,五百多个生态扩展里的大量 C 扩展,就是靠这些接口和内核一起工作。 每逢 PG 大版本升级,扩展都得重新验证兼容性,其中不少还需要专门适配。

pgrust-ext.webp

Rust 重写者面对这份合同,没有免费午餐。要么做一层兼容 C 的适配层,把旧接口接回来,同时吞下 unsafe 边界和长期兼容成本; 要么把扩展一个个重写,接受生态断裂。所谓折中,也只是按扩展分别选择这两种账单,不会让成本凭空消失。

pgvector 这种扩展还算好办,万行上下,规模相对可控。PostGIS 就完全不是一个量级,自己就有一两百万行代码,还有无数依赖。 而且 GIS 不是可有可无的长尾能力。没有 PostGIS,大批用户根本不会看你第二眼。pgrust 目前移植了十二个 contrib 模块; 而 PG 生态里有 1600 多个扩展,实用的也有五百多个。这两个数字当然不是严格的同口径比较,但也足够说明:从核心 contrib 走到完整扩展生态,后面还有很长一段路。

当然,我也不想拿“重写成本太高”当永久结论。AI 正在让写代码的工时快速贬值。 今天一个人能在 13 天里翻完 PG 内核,再过几个模型代际,把 PostGIS,甚至把整套扩展生态和依赖都翻一遍,也不是完全不可想象。 凡是只靠“太贵、做不完”来反对的观点,保质期可能都不会太长。

所以真正的问题是:翻译完,洗完代码后,你拿到了什么?

答案是:240 万行没有任何人类通读过的代码,对每一个上游项目的追赶义务,以及零信任积分与生产履历。


六、代码在通缩,信任没有
#

“完全兼容 PostgreSQL”,并不等于能自动继承 PostgreSQL 的信任。Aurora 能够大规模商用,不只是因为兼容 PG,更因为背后站着 AWS。 兼容性告诉用户:“它大概率和原来一样工作。”AWS 则回答了另一个更现实的问题:“出了问题,谁来提供长期支持、承担责任?”

再看 Greenplum。原 Greenplum 开发者早在 2022 年就基于 Greenplum 7 启动了 Cloudberry,2023 年将其开源; 到 2024 年 Broadcom 归档 Greenplum 的公开仓库后,Cloudberry 又承接了一部分原有用户和开发者,并进入 Apache 孵化器。 名字和治理结构一换,信用也得接着攒。pgrust 当然也一样:兼容性可以借,生产信任还得从零积累。

fork 还有个绕不开的矛盾。不改,你为什么要 fork?因为你想要做差异化价值点。可改得越多,能从 PostgreSQL 继承的兼容性和信任就越少。 pgrust 路线图上的线程化、无 vacuum 存储、列存,好像都是很有吸引力的卖点,也个个都在把它推得离 PostgreSQL 更远。

它越成功地长成自己的样子,就越不能靠 “我是 PostgreSQL 继承人” 来取得信任。 这条路不是没人走过。openGauss 从第一天起就是线程模型, 也就是 pgrust 最想做的头号架构改造。代价之一,正是扩展生态断裂。

openGauss 不能直接安装上游 PostGIS,GIS 能力靠超图开发维护的 Yukon(禹贡)补上。 这个适配从 2020 年 9 月启动,到 2022 年 6 月才正式发布。一家专业 GIS 厂商,光是“让 PostGIS 跑起来”就花了将近两年。 有专业 GIS 厂商投入,再加上实打实的采购需求,最后仍然免不了一件事:内核往前跑,GIS 适配在后面按版本追债。

这也顺手回答了很多国产数据库“自研内核”的问题。AI 确实能把研发成本打下来,但信任和生态合同不会跟着过来。 与其掀掉地基重来,不如在扩展、发行版和工具链上做增量,把新价值叠在现有生态上。这条路看起来没那么革命,通常却更能活下来。

老冯自己就在这一层干了五年。Pigsty 的构建、打包和整套工具链全都开源,没有一行私有代码。 看上去可以轻松 fork 一份,可它带不走五年攒下来的用户信任、扩展编目和供应链经验。我敢把代码全摊开,正是因为护城河从来不只是代码。


七、pgrust 真正值得留下什么
#

说了这么多,并不是要等着看 pgrust 的笑话。相反,我觉得它最有价值的去处,本来就不在“再造一个 PostgreSQL”。

pgrust 没必要急着被当成产品,作者本人也没把它说成成熟产品。把它当成一套实验装置,反而更合适: 这么大规模的移植,正好可以测一测 PostgreSQL 三十年的知识里,有多少已经被固化进代码和回归测试。

跑通全部测试,只是起点。接下来如果它在真实负载里踩到一个原版 PG 不会踩的坑,那就说明现有考卷漏掉了一条隐含规则。 这样的失败,比再多一个“测试通过”更有信息量,因为它可以变成 PostgreSQL 上游的一道新题。

遗憾的是,代码不能直接送回去。pgrust 选择了 AGPL-3.0; 除非相关权利人另行授权,PostgreSQL 上游无法在保持 PostgreSQL License 的前提下, 把这些 AGPL 代码直接并进主树。也就是说,发现可以回流,代码却不能直接照抄。

其实更值得让 AI 做的事,已经摆在眼前了。PG 三十年修过无数 bug,其中很多是“修了,但没留下考题”, 特别是微妙的时机型问题。修复为什么这样做,散落在邮件列表和 commit message 里;老开发者一批批退休,很多上下文也跟着消失。

让 AI 去考古 pgsql-hackers,把这些历史教训整理成回归测试和 isolation spec, 把老开发者“只可意会”的经验写成机器可以反复执行的用例,这才是真正对 PostgreSQL 有长期价值的方向。

我在《开源的业力》里写过:agent 时代最有价值的社区贡献,是一个可复现的失败。 修复正在变便宜,稀缺的是可复现的失败。PR 的继任者,也许是 failing test。

从零重写,很容易把三十年的默会知识扬掉;把历史里的坑变成测试,才是在给下一个三十年留下东西。


尾声:考卷可以复印,阅卷人不能
#

回到标题党最爱问的那个问题:AI 重写了 PostgreSQL,然后呢?

然后我们得到了一次昂贵但很诚实的实验: 代码迁移的成本已经低到不可思议,一个人借助 AI,13 天就能产出 240 万行 Rust 译文。 可 PostgreSQL 花三十年攒下来的信任,并没有跟着代码一起复制过去。

有意思的是,项目作者其实比围观者清醒。他亲手停掉了从零重写的第一版,转而拿 PG 的行为做锚、用 PG 的考卷判分。 他很清楚自己最后做成的是一次翻译,一次 code laundering,而不是凭空造出了另一个 PostgreSQL。

这个实验并非没有价值,pgrust 证明了在 SOTA 模型的支持下,通过语言翻译平移一个复杂项目是可行的。 把 C 写的 PG 翻译成 Rust 可能属于无用功,但是把一大堆 Java,Python 写的项目翻译成 Go,Rust 是有明显收益的。

提交历史里还有个细节我很喜欢:git 作者一栏中,Claude Fable 5 规规矩矩在 832 个提交上署了名。一个 agent 开始在 Git 里拥有独立的贡献记录。 《开源的业力》结尾提到过,市场迟早会逼着 AI 个体化,因为信用总得落到一个账户上。这 832 个署名,至少说明这件事已经开始了。

AI 能把开发工时压到极限,却压不短信任要走的日历。代码可以翻译,业力还得自己攒。

考卷可以复印,阅卷人不能。


参考阅读

相关文章