由 John Doe 九月 14, 2026
Tom Lane 近期谈到了 PostgreSQL 的进程架构、多版本管理机制,以及 AI 对开源社区的影响。
目录

作者:Elizabeth Garrett Christensen
我们正在庆祝 Postgres 诞生三十周年。借此机会,我们与项目核心开发者汤姆・莱恩(Tom Lane)进行了对谈,聊聊那些收获了成功的架构赌注、早期的混乱局面,以及云与 AI 时代下 Postgres 的未来方向。 本次访谈也提供视频版本。
PostgreSQL 作为开源项目已走过三十载。我采访了核心团队成员、拥有 25 年提交经验的汤姆・莱恩,一同回顾过去三十年间塑造这个项目的技术与架构决策。
正如你在访谈中会看到的,三十年来 Postgres 的架构与社区始终保持稳定。尽管系统持续迭代优化、特性不断完善,但整体而言,它依然是我们最初启动的那个数据库项目。项目的核心目标从未改变:绝不丢失数据、尽可能快地从崩溃中恢复、通过扩展与新特性满足现代业务的需求。
开场
伊丽莎白:我知道你在 Postgres 三十年的历史中,作为核心提交者参与了约二十五年。对于不了解你的读者,能不能描述一下 2026 年的今天,你日常参与项目的工作是什么样的?
汤姆:嗯,其实和 90 年代相比没太大差别。这个项目基本上一直靠邮件驱动,现在依然如此。我大概三分之一的时间用来收发邮件,三分之一审阅其他人的补丁,剩下三分之一开发自己的补丁。当然每天的情况都不尽相同,但大致是这样的节奏。
伊丽莎白:你是百分之百投入在 Postgres 核心项目上,对吧?除了核心项目之外,没有其他工作?
汤姆:没错。
伊丽莎白:好的,我准备了很多深入的技术问题,不过作为热身,我们先来几个快问快答轻松一下。你用什么 IDE?核心开发工作都用什么工具?
汤姆:Emacs。
伊丽莎白:工作的时候你会查 Postgres 文档或者代码注释吗?还是说这些内容都已经记在脑子里了?
汤姆:不,我经常查文档,查代码的时候更多,因为代码注释里有大量信息从来没整理进文档里。我脑子里大概知道代码树里各个部分的位置,所以很多时候找东西比查文档快得多。
进程 vs 线程
伊丽莎白:当你连接到 Postgres 时,系统会为你单独生成一个操作系统进程。你有专属的查询执行器、独立的内存空间,与其他连接相互隔离。而 MySQL、SQL Server、Oracle 等其他数据库采用线程模型,所有连接共享进程与内存空间。这是深入研究 Postgres 内部原理的人经常讨论的话题。Postgres 从这种进程隔离中获得了哪些线程模型无法实现的优势?又有哪些权衡?
汤姆:我认为我们从中获得的最核心的东西是代码简洁性。当你编写一段线性执行的代码时,不用担心中途有其他线程篡改你的数据结构。当然在共享内存区域我们还是要考虑这个问题,但那只占系统整体数据集的很小一部分。
另外在崩溃容错方面也有优势:如果某个会话进程出问题崩溃了,我们不用担心它破坏了 Postmaster 主进程的状态 —— 所以可以直接重启系统,并且确信数据依然完好。
所以这个模型确实有它的优势,但也有劣势,这点我们后面可能会聊到。现在有人在尝试转换为线程模型,但这是一项极其庞大的工程,我也不确定最终能不能成功。

图 1:Postgres 每连接一进程模型
伊丽莎白:你对这个讨论有什么特别的看法吗?你更偏好我们现在的模型,还是说对这个问题持开放态度?
汤姆:我没有特别参与线程化的相关工作,大部分重活都是其他人在做。显然,线程模型会带来一套不同的权衡、不同的优缺点,但我们确实应该推进这项工作,看看能不能得到比现在更好的方案。
内存模型
伊丽莎白:因为每个连接都是独立进程,每个后端进程都有自己的内存空间。同时还有所有后端进程都能访问的共享内存 —— 包括缓冲区高速缓存、锁表、事务相关数据等等。能不能深入讲一下共享区域和私有内存分别存放什么,以及这种划分如何影响系统对内存各部分的使用方式?
汤姆:好的。首先就像你刚提到的,共享缓冲区高速缓存。普通表的所有数据在被操作时,都会放在这个共享缓存里。所以每个会话看到的同一个数据页的视图都是一致的,这对于数据正确性至关重要,原因不言而喻。
另一方面,临时表存放在每个会话本地的缓冲区中,这也是为什么你无法访问其他会话的临时表。但作为代价,处理临时表数据时不需要考虑锁的问题。所以这既有性能上的优势,也有劣势,比如你不能依赖后台清理进程(VACUUM)来清理你的临时表。
还有一个值得一提的例子:每个会话都有自己的缓存,保存着当前需要用到的系统表数据的副本。这对我们来说非常有用,因为它让我们能以非常直接的方式处理不同会话对系统表内容有不同视图的情况。举个例子,你可能对一个表做了未提交的 DDL 修改,比如添加一列。如果共享系统表缓存,处理这种情况会相当麻烦。但在我们的模型下,做出未提交修改的会话能在自己的缓存里看到改动,其他会话则完全看不到。

图 2:Postgres 内存布局
伊丽莎白:太有意思了。再聊聊临时表吧,它们是什么,又是怎么创建的?我更多是在数据加载的场景下用临时表,很少在事务里用。
汤姆:我记得我们不支持把临时表转换成普通表,反过来也不行。但创建方式很简单,你只要写CREATE TEMP TABLE就行,其他和CREATE TABLE完全一样。表的内容会在整个会话期间存在,除非你主动删除它。当会话退出时,表里的所有内容都会被自动清理。它本质上就是用来存储会话结束后就不需要的临时数据。
预写日志(WAL)与崩溃恢复
伊丽莎白:Postgres 有一点让很多人感到意外:尽管可以有多个进程并发写入数据,但每一次 INSERT、UPDATE、DELETE 操作都会并行生成预写日志(WAL)记录。同时,只有单个进程能执行崩溃恢复并重放这些日志记录。能不能讲讲底层的架构,恢复进程和预写日志是如何工作的,这对 WAL 系统的设计有什么影响?

图 3:Postgres 崩溃恢复示意
汤姆:这么设计的核心目的,是减少事务提交前必须执行的磁盘写入次数。思路是这样的:每当你要更新表、索引里的某个数据页时,在真正修改共享缓冲区之前,必须先向预写日志写入一条记录,说明你即将做什么操作。
然后你再去执行修改。如果系统在修改写入磁盘之前崩溃了,恢复的时候我们就可以重放 WAL,把修改重新应用到数据上。这么做的好处是,你不用把待写入的数据散落到各个表、各个索引里,只需要把这一串日志数据写入并持久化就行。
一旦确认 WAL 数据已经写入磁盘,你就可以安全地提交事务,哪怕共享缓冲区里还有一大堆数据没来得及刷到磁盘上。所以本质上,这一切都是为了让磁盘 I/O 更友好。
我们最终还是要把那些表数据刷到磁盘上,但不用立刻做,而是等到检查点(checkpoint)的时候再执行,检查点不会太频繁。而且这是后台操作,事务提交不需要等它完成。

图 4:Postgres 预写日志流程
伊丽莎白:我听说很多人会调整检查点超时时间来管理 I/O。
汤姆:是的。不过这方面我没有太深入的实践经验。我知道它的工作原理,但不清楚最优的参数设置在哪里。
你提到恢复重放只有单进程。我觉得一方面是因为一直没人去做并行化,另一方面也是想让恢复机制尽可能简单。我们必须确保系统一定能恢复正常,所以不希望重放逻辑里藏着什么 bug。也许未来某一天会做并行化,但目前没人在主动做这项工作。
连接扩展性
伊丽莎白:我们一直在聊每连接一进程模型。如果单个进程出了问题,它会自己退出,不会影响其他会话,隔离性非常好。但在我所处的场景里,应用非常繁忙,有大量应用服务器,数千个连接同时连到一个 Postgres 数据库。社区主要靠外部连接池(比如 PgBouncer)来解决这个问题。你怎么看待这个现状?崩溃隔离确实有好处,但连接扩展带来了额外开销。目前的连接池方案是最优解吗?还是说 Postgres 正在做一些长期的架构改动?

图 5:Postgres 连接池模式
汤姆:嗯,大家确实在思考这个问题。这里有个鲜为人知的事实:其实一个进程崩溃的时候,我们还是会把所有其他进程都杀掉,因为我们没法完全确定这个进程在崩溃前有没有破坏共享内存里的数据。
所以实际发生的情况是,Postmaster 会杀掉所有其他子进程,重建共享内存,然后才允许启动新的子进程。所以从崩溃恢复的角度来说,只要把 Postmaster 作为独立进程就行。就算所有实际工作都在一个大进程里用线程完成,从崩溃恢复的角度看效果也是一样的。
要实现线程模型,我们需要解决我之前提到的那些问题,比如如果能支持更多会话当然很好,但难点在于每个会话都有自己的系统表缓存。在缓存里积累足够的数据之前,会话的执行速度不会太快,因为每次要获取新的元信息都得去查系统表。
正因为如此,一个会话把缓存填充得差不多之后,才能高效地处理工作。它算是一个比较 “重” 的对象,你不会想用一次就丢掉。所以大家才会采用连接池的方案,让同一个后端进程依次处理不同应用线程的查询。
要突破这个现状,我们就得解决如何在会话间共享缓存的问题,而不同会话对数据的视图可能是不一样的。有人在研究这个问题,但这是个相当棘手的难题。我不确定我们离解决方案还有多远。
线程模型与现代硬件
伊丽莎白:现在的服务器规模非常大,大型实例上 128 核、256 核都很常见。你认为线程化和现代硬件的发展方向最终会走向哪里?
汤姆:当然,你可以启动 100 到 200 个活跃的后端进程,只要内存足够,运行起来都还不错,毕竟每个会话都需要相当数量的工作内存。但现在的服务器内存都很大,所以这不算太大问题。
真正的瓶颈出现在你试图运行更多会话的时候,主要是上下文切换的开销。因为每个会话都有自己的内存映射,每次切换都要把它加载到 CPU 缓存里。
切换到线程模型的期望就是能降低这部分开销,因为所有线程共享同一个地址空间。所以往这个方向发展肯定是有意义的,但我们还需要很长时间才能实现。
Postgres 成功的关键
伊丽莎白:回过头来看,你认为哪些架构决策起到了至关重要的作用,让 Postgres 从一个小型的学术数据库项目,成长为市场上最受欢迎的关系型数据库?
汤姆:我想指出一点,就是系统的相对简洁性,而这正是源于我们没有采用线程模型这类选择。这其实不是我们刻意选的。90 年代末我们刚开始做这个系统的时候,根本没法考虑用线程,因为那时候线程还没有标准化,我们想支持的每个平台实现方式都不太一样。当然这二十年情况变了,但在当时这是个很大的障碍,让我们根本想都不用想。但这也给我们带来了简洁性,意味着开发者不需要太多背景知识就能上手参与开发,这对项目帮助很大。
另外我认为一个重要的里程碑是:我们从伯克利拿到代码的时候,系统根本没有预写日志。一旦崩溃,你经常只能从备份重新初始化数据库。所以加入 WAL 机制我认为是巨大的进步,大幅提升了可靠性,也让大家更愿意相信它能用于生产环境。
MVCC 的权衡
伊丽莎白:Postgres 通过多版本并发控制(MVCC)来处理并发读写。当一行数据被更新时,Postgres 不会覆盖原数据,而是在表中写入一行全新的版本,行头包含事务元数据。写入进行时,读取者可以访问旧版本的数据。最终会产生过期的旧行,通过 VACUUM(清理)操作来清除。其他数据库的实现方式不同,它们采用某种撤销日志的机制。你从架构层面怎么看 MVCC?你觉得这是个好的设计吗?有没有讨论过换一种实现方式?

图 6:Postgres MVCC 示意
汤姆:没有,我认为这从根本上就是个好设计。原因在于,那些必须做的维护工作都被推到了后台进程里,不会干扰用户实际的业务操作。
而基于撤销日志的系统,比如你要中止事务的时候,就得回退并重放撤销日志。所以如果所有事务都正常提交,那开销还不算大。但问题依然存在:你要怎么在不阻塞其他操作的情况下,提供一致的数据快照?
所以我认为这是个好设计,因为它把大量维护工作都放到了后台进程里。当然我们在 VACUUM 上投入了巨量的工程精力,而且还在持续投入。但我想就算没有 MVCC,我们也得用别的方式去解决同样的问题。
伊丽莎白:我记得 Postgres 19 会支持并行 VACUUM,对吧?
汤姆:是新特性。我没太密切关注这块。不过梅勒妮(Melanie)他们确实在 VACUUM 上做了很多工作。
我记得 2000 年左右有一次对话:在一个展会上有个人过来,一看就是资深的 Oracle 专家。他跟我说:“你们做对了,我们做错了。” 我一直很爱听这句话。
也许他就是客气一下,谁知道呢。但总的来说,我对这个方向很满意。这就是我们的实现,而且运行得相当不错。我们也知道该怎么持续优化它。
伊丽莎白:我觉得自动清理(autovacuum)也非常重要,不用用户自己去配置清理进程。现在 Postgres 基本上会自动处理这些,除非是特别大的数据库或者特殊场景。
汤姆:是的,这方面我们也做了大量工作,优化自动清理的启发式规则,判断什么时候该执行清理。这项工作还在继续。但总的来说,我认为成果还是很不错的。
Postgres 查询规划器
伊丽莎白:我知道你在查询规划器方面做了很多工作。当你编写一条 SQL 查询时,Postgres 查询规划器会尝试大量不同的执行策略,决定是全表扫描还是走索引,是用嵌套循环连接、哈希连接还是合并连接,以及表连接的顺序。它依靠内部的统计信息和代价模型来选出最优的执行计划。这也是 Postgres 的核心优势之一。能不能从高层给大家讲讲查询规划器的工作原理,难点在哪里,以及未来的工程方向?
汤姆:嗯,就像你说的,它本质上就是评估一大堆不同的执行策略,然后根据代价模型选出成本最低的那个。所以第一个问题当然就是,代价模型并不总是和实际情况吻合。第二个问题是,一旦连接的表超过很少的数量,可能的执行计划数量就会呈指数级增长。

图 7:Postgres 查询流水线
所以到那个时候,我们就只能依靠启发式算法搜索部分计划空间,这有时会导致我们找不到好的执行计划。所以我们一直都想优化代价模型,也一直在想办法减少规划器的执行时间。而这两个目标某种程度上是冲突的。所以这里面涉及大量复杂的工程权衡,要在合理的时间内生成好的查询计划。这种复杂性也正是我觉得这个领域有意思的地方。
伊丽莎白:短期或者长期来看,查询规划器会有重大更新吗?还是说都是小步迭代的优化?
汤姆:我现在没在做这方面的大改动。其他人接过了这部分工作,比如大卫・罗利(David Rowley)、理查德・郭(Richard Guo)他们,都在这个领域做了很多重要的工作,这很好。不过查询规划器依然是我的初心,如果有机会我还是愿意去做的。
C 语言与内存上下文
伊丽莎白:Postgres 大约有 150 万行 C 语言代码,是一个庞大的单体代码库,没有拆分成独立模块。项目没有采用标准的 C 语言内存管理模式,而是使用内存上下文机制:你把内存分配到绑定了查询或事务的命名上下文树中,当工作单元结束时,整棵上下文树会被一起释放。现在工程界都在讨论 Rust 这类内存安全语言。你怎么看待内存上下文模式,作为编译器强制内存安全的替代方案?你认为 Postgres 会继续作为一个大型 C 语言项目发展,还是会考虑重写部分模块或者做拆分?
汤姆:我没看到多少重写的意向。大概 20 年前我们就讨论过要不要用 C++ 重写,探索了一阵之后觉得投入产出比太低。我觉得现在就算有了新的语言,情况也没好多少。
关于内存上下文,要理解的是,系统里到处都是不同生命周期的上下文。有些和整个会话生命周期一样长,有些和查询一样长,还有些在查询处理每一行的时候都会重置。
核心原则就是,尽量把内存分配到生命周期最短的上下文中。只要能做到这一点,你基本就不用操心内存泄漏了,因为就算你忘了手动清理,内存也会在合理的时间内被自动释放。
实际上,在我们的代码里,逐个释放内存反而是种反模式,因为上下文批量清理的效率比逐个释放高得多,而且也更健壮,就算你忘了释放某个对象,它最终也会被清理掉。
这种模式的缺点在于,如果你工作在一个长生命周期的上下文中,那忘释放内存就真的会出问题。而且我们常常把这种思维惯性带到不适用的场景,然后就会出现内存泄漏 bug,需要去修复。所以它也不是没有缺点,但整体来说非常契合我们的需求。
这个概念大概是我发明的,至少大部分是我做的。所以可能我有偏见,但我觉得它很好用。
除了长生命周期上下文的泄漏问题,还有一个问题:在一个上下文中构建的数据结构,可能包含指向另一个更短生命周期上下文中数据结构的指针,这样就会出现野指针问题。所以它也不是完美无缺,但非常适合我们的场景。而且我并不认为语言强制的内存安全模型能比这做得更好。我没仔细研究过那些语言,所以不敢说死,但我对此持怀疑态度。
扩展生态
伊丽莎白:我想聊聊 Postgres 的扩展生态。现在开发者圈子里有个很大的趋势,就是用 Postgres 做所有事情:“用 Postgres 就够了”。你可以把它当键值存储用,可以用 LISTEN/NOTIFY 做消息队列,可以做成任务调度器,也可以当文档数据库用,能做的事情太多了。大家的口号是,为什么要跑五个不同的数据系统,一个 Postgres 不就够了吗?作为专注于核心数据库的开发者,你喜欢这种多功能性吗?你觉得这是好事,还是担心人们把 Postgres 用在超出它设计初衷的场景?
汤姆:嗯,可扩展性从项目最开始就是核心理念之一。伯克利的前辈们设计它的时候,就支持添加新的数据类型,也支持新增索引访问方法、构建新的索引类型。
从那以后我们延续了这个思路,又增加了更多扩展系统的方式。举个例子,如果一个新特性只能支持核心数据类型,没法扩展到支持扩展的数据类型,那我们大概率不会接受这样的实现。我们会让开发者回去想想怎么把它做成可扩展的,再回来提交。
我认为这一点对项目的成功至关重要,也吸引了大量开发者围绕核心 Postgres 构建各种东西。所以我绝对是支持的。当然,有些任务关系型数据库本身就不适合,但很多时候大家会发现 Postgres 已经足够好了。这样就不用再集成其他工具,在很多方面都更方便。
比如你提到的 LISTEN/NOTIFY,它是个很好的简单通知机制,但扩展性不算太好。如果你要传输海量消息,那还是得找专门的消息系统。但对于很多应用来说,它完全够用了。
解析器扩展性与扩展的边界
伊丽莎白:再聊聊扩展在 Postgres 内部的工作原理。你可以添加新的数据类型、新的索引类型、新的操作符,甚至可以在 Postgres 里加入过程式语言。这些都不用修改核心 Postgres 引擎,只要做扩展就行。但据我所知,扩展不能修改 SQL 解析器。所以如果你想要新的关键字、新的语法子句,就得给核心 Postgres 打补丁。而要把补丁合入核心是个大工程,可能要经过多年的评审,还要等大版本发布。比如 pgvector 这个 AI 向量扩展,就是完全基于现有 SQL 语法实现所有功能的。大家对这件事怎么看?有没有讨论过让解析器也支持扩展?
汤姆:是的。这本质上是因为我们用 Flex 和 Bison 来构建解析器。这些工具读取静态的语法描述,生成解析表,然后编译进服务器里。它就是这样工作的,没法运行时修改。
原则上我不反对可扩展解析器。实际上,在 Postgres 诞生之前的 70 年代,我就做过类似的系统。但从那段经验来看,可扩展解析器比你想象的难得多。Bison 的好处在于,如果你的语法有歧义,它会直接告诉你,让你修复。这样运行时就不会出现意外,比如某个行为被另一条有歧义的语法给屏蔽了。
所以如果我们要找一个新的可扩展工具,我会先问几个尖锐的问题:怎么保证同样的正确性?
Bison 还有个好处:它成熟、文档完善、调试充分、免费,而且所有平台都能用。我不知道有没有其他工具能同时满足这些条件。但正如你所说,这确实是扩展性的一个短板。所以如果能找到更好的方案,我完全支持。
汤姆・莱恩的 Postgres 之路
伊丽莎白:我今天采访前做了点功课,网上说你最开始做 Postgres 是因为需要一个数据库来存股票交易信息,你从用户、bug 报告者开始,提交了几个补丁,然后一步步成为核心提交者。如果这个故事不对的话请纠正我。这个过程是怎么发生的?你第一次看到这个项目的时候,有没有想到自己会花三十年在上面?
汤姆:没有,完全没想到。嗯,那个故事基本是对的。我当时需要一个数据库,又不想花钱买 Oracle。那时候比较靠谱的选择就是 Postgres 和 MySQL,我看了 MySQL 的代码库,觉得完全不想碰。
伊丽莎白:这个我得回头再好好问问。
汤姆:我看了 Postgres 的代码,结构要好得多。所以我们就选了它,然后马上就遇到了一些 bug,提交了修复补丁,慢慢就深入进去了。
在那之前,我对数据库一窍不通。我读研究生的时候,教授们根本没人关心数据库。所以我后来发现这里面其实有很多有意思的问题,然后就越陷越深了。
伊丽莎白:太有意思了。这个项目从加州大学伯克利分校作为开源项目发布没几年你就加入了。你刚接触的时候,Postgres 是什么样的?有没有什么你知道必须改的大问题?
汤姆:系统的骨架是好的,不然我们根本走不到今天。但确实有一大堆细碎的 bug,需要我们去定位修复。那个阶段持续了好几年,才真正做到系统基本没什么 bug。
主要功能方面,我觉得那时候唯一缺失的就是崩溃恢复。我们之前聊过,预写日志机制解决了这个问题。除此之外,我参与的整个过程里,另一个贯穿始终的主题就是不断提升性能。
伊丽莎白:当然。不过那时候它的性能应该也还可以,不然你也不会用它。你早期的项目真的用 Postgres 来做交易吗?
汤姆:是的,我们确实用它做了一段时间的股票交易。我们有一些市场模型,直到它们失效为止。之后我们就不做这行了。
为什么 Postgres 能走过三十年
伊丽莎白:过去三十年数据库领域发生了太多事,和它同时代的很多流行数据库都已经淡出视野,或者被其他项目吸收了。对于任何软件项目来说,三十年都足够漫长,还能保持像 Postgres 这么受欢迎更是难得。你认为是哪些早期决策,让 Postgres 作为独立项目存活了这么久?
汤姆:在我看来,毫无疑问是伯克利给它的宽松许可证。它允许任何人以任何方式使用代码,甚至可以基于它做专有分支。很多人都这么做了。但关键在于,因为这完全合法,大家都心知肚明,所以他们依然会和社区项目保持联系,把自己的工作贡献回来,毕竟他们也希望有一个稳固的基础可以构建。
开发者会在专有产品和社区项目之间来回切换。我认为早期很多工作都是靠这种模式支撑的:大家在社区版 Postgres 之上做产品售卖。
另一个同时代的例子可以看 Linux。他们走了不同的路线,用了 GPL 许可证,权衡点不一样,但同样是大家都清楚规则的模式,并没有阻碍人们为项目贡献代码。
伊丽莎白:我同意,我经常聊 Postgres 的时候都会提到许可证,因为我觉得它太重要了。很多人不仅能贡献代码,还能基于它创办整个公司。我觉得 Linux 和 Postgres 虽然运作模式和许可证略有不同,但两者互相成就。Linux 这三十年屹立不倒,它作为操作系统的流行度,加上 Postgres 在 Linux 上的良好适配,也是成功的因素之一。
伊丽莎白:你参与过许可证相关的事情吗?据我所知,Postgres 有自己的许可证,即 PostgreSQL 许可证,我觉得和 FreeBSD 许可证很像。
汤姆:是的,在我们看来,其实研究过的人告诉我,它更接近 MIT 许可证。虽然出自伯克利,但和 BSD 许可证不太一样。反正我作为非法律人士的理解就是:你可以随便用这段代码,只要别告我们就行。
伊丽莎白:我觉得像亚马逊、谷歌、微软这些大型云厂商,都能基于 Postgres 打造完整的业务,提供 Postgres 云服务…… 这也推动了它的普及,尤其是在我们如今的云时代。没有限制,所以很多人都愿意去托管它,帮用户运维并收取费用,这也进一步提升了它的知名度。
汤姆最喜欢与最不喜欢的版本和特性
伊丽莎白:你最喜欢的 Postgres 版本是哪个?
汤姆:这很难说。通常我最喜欢的都是最新版本。不过回顾我们刚才聊的,我觉得 8.0 是一个高峰,因为我们加入了 WAL 日志恢复,也就是前面说的原因。那是一个巨大的进步,让它从一个玩具变成了真正严肃的数据库。反过来的话,我可以明确告诉你最不喜欢的版本:13 版本,bug 简直多到爆炸。我们之后花了好几年修 bug。绝对是问题最多的一个版本。
伊丽莎白:13 版本是哪个特性导致这么多问题吗?
汤姆:万幸我已经记不清细节了。
伊丽莎白:有没有哪个你主导开发的特性,让你特别自豪?
汤姆:我觉得我在 Postgres 的工作,是大量零散的小改动积累起来的。我可以挑出这个那个,但单独拿出来可能都算不上什么大特性。
伊丽莎白:如果可以不用考虑向后兼容,你明天最想从 Postgres 里删掉哪个特性?
汤姆:我不太喜欢我们现在的分区实现方式。我也没想出更好的方案,但它确实很乱。
加入 Postgres 之前的工作
伊丽莎白:我知道你加入 Postgres 项目之前的一些经历。我想你做过 JPEG 相关的工作:图像标准。网上说你参与过 libjpeg 的开发,就是火星毅力号相机用的那个库。给我们讲讲这段经历,还有它对你做 Postgres 有什么影响?
汤姆:我没有参与 JPEG 标准的制定。但标准出来之后,大概有十几个人感兴趣,说我们来写一个开源实现吧,然后就做了,也就是后来的 libjpeg。最开始的时候大家都很积极,十几个人一起做,之后就慢慢进入维护模式了。我当了大概五年的主要维护者,所以上面我的名字比别人多。
后来我投入了 Postgres 的工作,就完全顾不上 libjpeg 了。很高兴后来有人接手了,在我冷落了它很久之后,他们接过去继续做了。
我可以肯定毅力号上的工程相机用了 libjpeg,因为乔・康威(Joe Conway)找到过一篇学术论文提到过。他们从来没直接联系过我。
伊丽莎白:开源图像标准和数据库之间有共通之处吗?
汤姆:直接联系没有,但它确实影响了我对软件许可证的看法。我认为 JPEG 如今无处不在,25% 是因为标准本身确实优秀,能让同质量的图片体积缩小到原来的十分之一;剩下 75% 是因为有一个人人都能用的免费实现。没有它,早期的浏览器就不会支持它,你也就不会在网上到处看到 JPEG 了。
我们当时做了正确的决定。后来到 Postgres 这里,它的许可证非常宽松,这也是吸引我的重要原因。
伊丽莎白:你是怎么对开源这个理念产生兴趣的?显然你对协作式开源软件项目充满热情。是在大学里接触到的吗?什么时候开始对这个想法感兴趣的?
汤姆:80 年代读研究生的时候就接触到了。在学术环境里,到处都是没人主张版权的软件,大家更愿意分享、改进、使用。所以那时候我就受到了这种思想的熏陶。
具体投身开源的原因是,我刚读完博士,学业基本上是美国纳税人资助的。我觉得我需要做点什么回馈社会。所以我想找一个开源项目,贡献给世界,也算稍微报答一下社会。然后就遇到了 libjpeg,我觉得挺有意思的,就参与进去了。
伊丽莎白:你的博士研究方向是什么?
汤姆:软件架构。论文标题其实还有几个词,但核心就是这个主题。
Postgres 的治理模式
伊丽莎白:我想聊聊项目的运作方式和治理模式。它有点特别,大多数同规模的开源项目运作方式都不太一样。它们通常有正式的基金会,有董事会,可能还有企业赞助商。比如 Apache 基金会、Linux 基金会、Python 软件基金会,或者 Kubernetes 的 CNCF。Postgres 不是这样的。据我所知,PostgreSQL 开发组甚至都不是一个正式的法律实体。和其他大型项目比起来,它更非正式一些,更像是自我选择的模式。有一个核心团队,提交者是自我遴选的,几乎所有工作都通过邮件进行。你甚至不能提交 Pull Request,Postgres 代码放在 Git 上但那只是镜像,一切都通过邮件发生。你怎么看待这种治理模式?
汤姆:说实话,我也不知道我们是怎么让它运作起来的。这其实是项目初期的条件决定的。我们从伯克利拿到了这么一段代码,我们不拥有它,也不能修改许可证。我们讨论过,最后觉得我们没有权利修改许可证,因为我们不是代码的原始或主要作者。
所以许可证就定下来了。然后我们有一群贡献者,谁也管不了谁,大家都为不同的公司工作。所以任何强势的治理模式都行不通,大家会直接走掉。
所以我们从来没有过正式的治理。最开始,核心团队就是拥有并运营 CVS 服务器的人,他们掌握着提交权限。但这也没持续太久,没多久我们就有了独立的基础设施团队。所以现在核心委员会其实没有任何正式权力。大家愿意听我们的,只是因为一直以来都是这样。但我也不确定,如果真的有一天为了项目发展方向吵得不可开交,会发生什么。但不管怎样,它就这样维持了三十年,我也不知道怎么做到的。
伊丽莎白:我觉得参与项目的人大多都对 Postgres 本身充满热忱。如果不是想让项目变得更好,没人会来写补丁。它发展不快,大补丁可能要好几年才能合入,小补丁也可能要等很久。但我觉得某种程度上这也带来了项目的稳定性,因为它不会为了追新潮就去做改动,也不会因为某个人或某家公司的兴趣就重写某个部分。这种 “全员共识” 的模式带来了很多稳定性。
汤姆:我觉得某种程度上这是领域特性决定的。大家都希望数据库是 “无聊” 的。把数据放进去,就知道明天一定能取出来。需求变化也没那么快。SQL 标准委员会每五到十年出一个新版本,加一些新东西,我们看看,可能会实现,也可能不会。没什么东西逼着我们急着做改动。对于我们做的事情来说,这样挺好。
Postgres 的未来
伊丽莎白:你认为 Postgres 接下来会往哪个方向发展?我们聊到了治理模式,有些同规模的项目会有路线图或者指导委员会,Postgres 没有。我们也聊了一些线程化的事情。还有没有其他可能会进入核心 Postgres 的重大转变?很多人问备份、高可用这些功能,你们有没有考虑把更多这类能力做进核心里?
汤姆:就像我说的,项目没有统一的路线图。这还是源于早期的状况:没人能命令别人做什么。你想做成一件事,就得说服大家你的想法是好的,然后也许他们会和你一起做。我们现在依然是这样运作的。当然,现在有些人受雇于公司,公司可以安排他们的工作方向,但要想把代码合进社区版本,还是得说服所有人。
我们已经聊过线程化,以及做这件事主要是出于性能方面的原因。另外一个持续了一段时间的方向是,大家希望能支持列式存储表,而不是现在传统的行式存储。我们已经有了表访问方法 API 的概念,但现在还不够通用,还没法支持别人实现列式存储表。但大家对这个方向依然很有兴趣,会继续推进。也许我们最终能实现。
除此之外,我个人并不主张把能作为扩展良好运行的功能都放进核心。我们的核心代码工程人力是非常有限的,代码越变越大,我们的精力就越分散。我一贯的观点是:如果能作为扩展很好地工作,那就保持扩展的形式。
AI 与 Postgres 社区
伊丽莎白:整场采访我都尽量没聊 AI,我都有点佩服我自己了,要是能在晚宴上全程不聊 AI 可太厉害了。但显然我还是很好奇,你的世界里 AI 有什么进展吗?你们有没有在用 AI 编码工具或者代码审查工具?
汤姆:我个人用 Claude Code 做过一点小事,但还谈不上深入使用。
整个社区还在讨论,要不要接受大量 AI 生成的提交。几周前我们刚开了一个工作坊,二十多个人聚在一起当面讨论了这个问题。我觉得那个小组的共识是:进入代码库的每一行代码,都必须有人类来负责,并且能解释其中的每一个决策。哪怕部分代码是 AI 写的。这还不是正式的项目政策,但我想很快会在更广泛的社区里讨论,争取形成一个标准化的规则。
有一件事已经实实在在影响到我们的工作流了:我们现在收到了巨量 AI 生成的 bug 报告和安全报告。很多都是后台自动生成的。这有点麻烦,因为其中确实有很多是需要修复的有效 bug,但也有大量是因为工具不理解基础架构概念而产生的无效内容。希望以后会好起来,或者我们能找到更有效的筛选方法。
伊丽莎白:我知道邮件列表现在还有点限制。所以据我所知,还没有机器人往邮件列表发帖,但也许有只是我没听说。你有没有见过黑客列表(开发者讨论代码的邮件列表)里有非人类发帖?
汤姆:我没见过能识别出来的。确实有越来越多人直接把 AI 生成的报告当 bug 报告发过来这种情况,但我还没见过看起来像是机器人订阅了邮件列表的。也许是我没注意到。
汤姆的开发环境
伊丽莎白:聊聊你的本地开发配置吧?
汤姆:我大部分工作都在 Linux 服务器上做,用 Emacs 和命令行工具。我实际打字用的是 Mac 笔记本,通过 SSH 和 X11 连到服务器上。
这个配置我用了很多年了,因为我之前有很严重的人体工学问题。我需要一个能让手放在腿上的姿势,所以笔记本放在腿上,手就在那里,然后看着前面单独的大屏幕工作。
伊丽莎白:用的什么 Linux 发行版?
汤姆:我以前在红帽工作过,所以现在主要还是用 RHEL。服务器现在是 RHEL 10,还有几台 Fedora 机器。
伊丽莎白:有没有试过新出的那些,比如 Alma Linux,CentOS 停更之后的新发行版?
汤姆:没有。我实际上是付费订阅红帽的。我依然相信他们做的事情,也觉得他们需要支持。
结语
伊丽莎白:你接下来有什么计划?Postgres 之外还有别的打算吗?
汤姆:没有,真没有。我想我会一直做 Postgres,直到我觉得无聊了,或者意识到自己跟不上了。不知道什么时候会发生,也不知道会不会发生,但现在我很享受做这件事。
伊丽莎白:非常感谢你今天的分享,汤姆。我一直很欣赏这个项目。我和你在好几家公司共事过,对这个项目有特殊的感情。谢谢你今天接受采访,也谢谢你为项目做出的所有贡献。
汤姆:也谢谢你邀请我。聊得很开心。
参考
Tom Lane on the Architectural Decisions That Shaped 30 Years of Postgres