博客

和 PostgreSQL 的二十年·3 篇,共 7

只有架构师和上帝知道

Cover Image for 只有架构师和上帝知道

项目跑了一段时间之后,有一天听说要拆库。

把原来的一个 PostgreSQL 实例,拆成两个。

为什么?架构师和上帝知道。


这不是在抱怨。那个年代的项目就是这样运转的——架构决策从上往下传,执行层拿到的是结论,不是推导过程。你知道要做什么,不一定知道为什么。

多年后回头猜,大概是把 BI 部分拆出去了,放到独立的库里。把分析和业务分开,是合理的架构演进。但当时没人跟我解释这个,我只知道:两个实例,跨实例的操作要保证事务一致性,这个问题现在是你的。

跨数据库实例的事务一致性,在 J2EE 体系里有标准答案:JTA + XA,两阶段提交


XA 是一个分布式事务协议。简单说,它在普通事务外面再套一层协调者——协调者先问所有参与者"你准备好了吗"(Prepare 阶段),所有人都说准备好了,再统一发出"提交"指令(Commit 阶段)。任何一个参与者说没准备好,全部回滚。

PostgreSQL 对 XA 的支持其实相当完整。PREPARE TRANSACTION 把事务挂在那里等待协调者的最终指令,pg_prepared_xacts 视图可以看到所有处于 prepared 状态的事务。协议本身没什么问题。

问题在于协调者。

如果协调者在 Prepare 之后、Commit 之前宕机了,那些 prepared 状态的事务就会悬挂在那里——既没有提交,也没有回滚,锁还在,资源还占着。这种状态叫做悬挂事务,是 XA 在生产环境里最让人头疼的问题之一。

悬挂事务怎么处理?找 DBA。

DBA 在哪里?在另一个房间。

我们能看到什么?什么都看不到。


这件事在第一篇里说过——生产环境对开发完全隔离,那堵墙一直在。XA 事务在生产里真正出了什么问题、悬挂事务发生过几次、DBA 是怎么处理的,这些我们一概不知。代码写完交出去,剩下的事情发生在另一个世界。

当时能做的是把 JTA + XA 的代码写对,写严谨,测试覆盖到,然后交付。生产里发生什么,不归我们管。

这不是推卸责任,是那个年代离岸开发团队的真实处境。


就在那段时间,有一次去总公司办事,无意间路过了 BI 团队的工位。

总公司派来负责这个项目的总经理自己也是技术出身,直接带着 BI 团队。他们做的东西我平时接触不多,那天碰巧瞥见了他们的一个内部工具——一个 Web UI,可以对业务库做快照,然后一键切换、备份、恢复到任意一个快照版本。

我在那里站了几秒钟,看了一眼,然后有人注意到我了,我就走开了。

那是脱敏的数据,是内部工具,是最高机密,不是我应该看的东西。

但那几秒钟留下来了。一个 Web 界面,数据库版本像 Git 分支一样可以随意切换——这个概念那时候我没有在任何地方见过,想都没想到数据库可以这样用。

我不知道他们怎么实现的,也没有资格去问。就这样走开了。


2022 年,Neon 发布。

Neon 是一个 serverless PostgreSQL 服务,它最核心的功能之一叫做 Branch——你可以给数据库创建分支,像 Git 一样,每个分支都是一个独立的数据库副本,可以随意切换、实验、恢复。

我第一次看到这个功能介绍的时候,脑子里闪过了那个画面。

那个 Web UI。那几秒钟。

他们在 2009 年就做了这个东西,自己用,内部用,然后那扇门关上了,我在外面走开了。

Neon 把它做成了产品,卖给了所有人,包括我。

十三年后,那扇玻璃门里的东西,在我自己的项目里跑着。


这个系列里有一条若隐若现的线:我总是在门缝里看到一点什么,然后门关上了。

第一篇,生产是黑盒,开发看不到里面发生了什么。第二篇,IBM 小型机是别人的,你只是排队用它。这一篇,架构师的决定不需要解释,BI 团队的工具是最高机密,XA 事务在生产里的真实表现没人告诉你。

每次都只看到了一部分。

但那些一瞥,都留下来了。有时候是一个概念,有时候是一个感受,有时候只是一个模糊的印象——某种东西应该可以做到,虽然当时不知道怎么做,也不知道有没有人做到过。

后来慢慢都找到了。有些用了几年,有些用了十几年。

那个 Web UI,用了十三年。