2025年6月19日

优化全球应用的Postgres

Postgres 使用一种冗长、有状态的协议将客户端连接到数据库。虽然它非常适合在本地网络上连接,但其设计早于无服务器部署、多区域应用程序和现代网络架构的时代。我们来谈谈在2025年连接到Postgres时遇到的挑战以及如何缓解它们。

Optimizing Postgres for Global Apps

泰勒·本菲尔德是Prisma的资深工程师,也是Postgres领域的一切专家,从高层架构到低层协议无所不精。他的知识和专业技能帮助Prisma Postgres成为市场上最具创新性的无服务器数据库。在本文中,他将分享他在Prisma Postgres方面的工作经验及其未来的愿景。

引言

那一年是1998年。我在Game Boy Pocket上玩《宝可梦 红》,吃着Dunkaroos零食,然后听着厨房收音机里传来我无视的NSYNC的背景音乐。与此同时,我不知道的是,互联网正迅速从学术领域过渡到家庭商品。随着互联网的普及,出现了一波工具,其中许多工具本身就是从研究项目中发展而来的,用于构建我们今天高度依赖的万维网。其中一种工具是PostgreSQL,一个在加州大学伯克利分校创建的下一代数据库引擎。1998年,Postgres线协议发布,正式规范了连接到Postgres数据库的规范。

我写这篇文章的时候是2025年。屏幕被Notion和我的得力AI代理分成两半。吃Dunkaroos可能会让我陷入血糖冲击。我目睹了Web从Geocities上的跑马灯标签,发展到只需一条命令即可在全球部署的全栈应用程序。AI正在使应用程序开发对所有人(不仅仅是那些有软件工程背景的人)都可访问,而对软件的需求从未如此之高。尽管发生了所有这些变化,Postgres这个90年代的数据库仍然是最受欢迎的数据库之一,并且仍在不断发展。

显然,我是一个怀旧的人,但这篇文章实际上是关于在现代应用程序开发世界中使用Postgres。尽管Postgres协议多年来不断发展,但对于当今流行的无服务器、全球部署的应用程序来说,它本质上是次优的。让我们探讨一下这些缺点以及我们如何为2025年及以后改进Postgres。

Postgres线协议

我会写很多关于线协议的东西,所以我们也许可以先谈谈那是什么。

什么是线协议?

线协议是用于通过连接边界以消息形式发送数据的规范。它概述了客户端和服务器的通用语言,并允许任何人构建兼容工具。

哪些技术使用线协议?除了像Postgres、MySQL和MongoDB这样的数据库,我们还有HTTP、WebSockets、gRPC、FTP、SMTP、SSH、TLS、MQTT以及许多其他缩写词。线协议是任何两个系统之间通信的核心。

在Postgres中,线协议是一种字节级二进制规范,通常通过TCP连接传输。这使得社区能够为支持TCP连接的每种编程语言和每种运行时环境构建客户端(✨预示✨)。在某些情况下,我们甚至看到新的数据库引擎采用Postgres线协议,以与Postgres客户端生态系统兼容。因此,Postgres线协议本身已成为一种标准。如此深入地根植于生态系统也使得采纳新事物变得极其困难,尽管有人尝试改进该协议以适应现代架构。

Postgres协议握手

Postgres线协议于1998年正式制定,旨在使Postgres能够被各种语言和运行时环境的客户端访问。当时的系统架构通常将Postgres和应用程序服务器部署在同一台机器上,或至少通过本地网络连接。那时还没有云计算提供商或无服务器运行时需要考虑。因此,Postgres线协议是一种冗长、有状态的协议,对现代应用程序架构有显著的缺点。

让我们分解一下典型的Postgres连接。这里我们将使用现代协议而不是1998年的原始协议。

在执行第一个查询之前,网络上总共有18条消息!我们再来看看如何准备查询。有两种方法:简单查询协议和扩展查询协议。应用程序通常使用扩展查询协议,因为它支持参数化查询以防止SQL注入,所以我们将重点放在那里。

仅仅执行一个查询就需要14条消息!对于那些“好吧,实际上”的读者来说,并非所有这些消息都是阻塞的。同一方向的多个箭头在不等待任何响应的情况下发送,因此从技术上讲,我们有4个I/O阻塞序列。准备阶段预计将在同一连接上重复使用不同的参数值,从而减少到无害的2个I/O序列:一个到服务器,一个返回到客户端,形成一个单一的往返。这还不算太糟!幸运的是,对于这篇博客文章来说,还有更多内容需要探讨……

Postgres协议显然是为能够维护长时间TCP连接的有状态服务器设计的。这种服务器正是1998年部署的类型。当连接可以重复使用数千次时,建立连接和准备查询的开销被降到最低。

现代应用程序正在部署到AWS Lambda、Cloudflare Workers和Vercel等无服务器运行时,这些TCP连接在每次请求时都会建立和断开。这种冗长的连接握手正在扼杀您的无服务器应用程序性能,而您对此无能为力。

我的悲观情绪不止于此!

光速

既然我们已经看到了Postgres线协议有多么冗长,那么我们再加入全球部署的因素。

让我们回顾一下上面提到的Postgres连接握手。那是建立连接的18条消息。其中一些被捆绑并乐观处理,使我们剩下大约5次往返才能完成握手。

我们再说,您的一个用户在日本东京访问您的应用程序,而您的Postgres数据库在弗吉尼亚(us-east-1)。由于您运行在Cloudflare Workers上,您的用户可以低延迟连接到您的应用程序🎉 然后您需要查询您的数据库💀 地理距离会随着往返次数而增加,导致您需要大约460毫秒才能建立连接。专业提示:不要让您的经理在国际旅行时运行Lighthouse评分。

连接池

所以你已经读过资料,发现连接池会保持热连接?让我来毁掉你的一天。

连接池确实保持热连接,但是您的应用程序仍然必须安全地连接到连接池。这是如何运作的?Postgres线协议。我上面概述的连接到Postgres服务器的序列与连接到PgBouncer等连接池的序列相同。连接池不会为连接延迟提供实质性的性能优势。

连接池对于保护您的数据库免受连接耗尽非常重要,但这并不是本文的主题。

全球可访问的Postgres

如果我没有很酷的东西可以分享,我就不会写这些了。

在Prisma,我们正在构建一个为现代应用部署而设计的Postgres数据库平台。它是真正的Postgres,运行在独特的裸机架构上,以简单的按查询付费定价实现令人难以置信的性能。您可以在我们的文档中阅读更多关于Prisma Postgres的信息。对于本文,我想重点讨论我们如何使Postgres适用于全球部署的无服务器应用。

滥用 优化Postgres协议

为了支持从任何Postgres客户端到Prisma Postgres的连接,我们不能偏离Postgres线协议规范。正如我上面概述的,这会带来与我们构建用于现代应用程序的最佳Postgres平台的使命相矛盾的缺点。所以我们发挥了创造力。

如果我们把协议中冗长的部分移到更靠近应用程序的地方,而不移动数据库呢?如果我们利用我们的网络骨干网更快地连接到数据库呢?如果我快点切入正题呢?

为了实现这一点,我们在整个裸机集群上运营着一个全球性的Postgres感知代理网络,该集群为Prisma Postgres提供动力。我们网络上的每个主机都可以接收来自客户端的入站Postgres连接,在我们的代理和您的应用程序之间保持低延迟的往返。最优代理通过基于延迟的DNS解析识别,因此您将始终连接到最快的代理,即使它在地理位置上不是最近的。随着我们扩展到更多区域,我们也将继续扩展我们的代理网络。

然后,我们将路由和认证数据分发到这些主机运行的每个数据中心。这允许代理终止与Postgres客户端的Postgres连接握手。所有这些冗长的通信现在都在更短的地理距离内进行,从而大大加快了往返速度。

我们必须建立从代理到数据库的连接,这可能是一个地理上较远的连接。现在,通过我们的高带宽网络建立连接,我们可以根据实际工作负载的测量结果无缝优化这些步骤。

总而言之,连接看起来是这样的

现在您可能会意识到这比原始消息更多。您是对的!地理距离和优化在这里很重要。客户端 → 代理是近距离连接,而代理 → Postgres 可能是地理上较远的连接。地理距离仍然存在,对吗?对。

同样重要的是,我们的代理服务器是有状态的。它们运行在具有高带宽连接的裸机硬件上。我们可以优化上游Postgres连接的重用,类似于连接池,并对连接握手进行我们自己的优化,因为Prisma同时操作连接的两端。在Prisma Postgres的直接TCP连接的预览版和正式发布版之间,您可以预期连接延迟会因这些优化而显著降低。

连接池(即将推出,适用于直接TCP连接)

连接池在这其中扮演什么角色?很快,我们将为直接TCP连接添加连接池。Prisma Postgres连接池将与数据库在同一台裸机上运行,提供连接池和Postgres之间的零延迟连接。这将通过减少由于连接峰值或空闲客户端连接而导致的数据库负载,从而实现更高数量的入站连接。

如上所述,连接池仍需与客户端协商连接,从而抵消了任何预期的连接延迟改进。除非握手被移到更靠近客户端的地方。这就是我们的代理再次发挥作用的地方。代理将自行池化连接,并根据客户端活动主动创建新连接。冗长的连接握手可以在后台处理,允许代理简单地将它已经建立的连接之一分配给上游连接池。换句话说,代理也充当了分布式连接池。

超越协议的扩展

到目前为止,我谈到了Prisma Postgres如何改善全球Postgres连接的性能,但对于不支持TCP连接的环境呢?你们这些懦夫,把TCP加到浏览器里去!

我知道。从前端连接到数据库是不好的。为什么不好?因为有人告诉你它不好?不,它不好是因为它暴露了原始数据库访问给任何使用你的应用的人。告诉你是不好的。不过,浏览器不仅仅用于你的应用前端。我们看到了一波基于浏览器的软件开发工具,包括GitHub Codespaces等IDE和v0等AI代理。由于浏览器没有低级别的TCP API,这些工具被迫通过它们的后端连接到你的数据库。

为了让Prisma Postgres更容易被基于浏览器的开发工具(再次强调,不是您的应用前端)访问,我们计划升级代理,使其也接受入站WebSocket连接。WebSockets可以像TCP连接一样中继二进制数据,这使得我们能够终止浏览器会话的连接,建立到Postgres的TCP连接,并通过两个通道中继二进制消息。这将允许在浏览器中运行的Postgres客户端(再次强调,用于开发工具)在连接到Prisma Postgres数据库时使用WebSocket作为底层传输。虽然TCP仍然是支持它的环境的最佳选择,但我们相信WebSocket功能可以在不损害Postgres协议功能的情况下扩展到其他环境。

与此同时,我们还发布了@prisma/ppg,这是一个轻量级的基于HTTP的客户端,用于查询Prisma Postgres,目前处于早期访问阶段。@prisma/ppg客户端非常适合简单用例,您只需执行查询,而不需要访问Postgres线协议的更高级功能,例如流式传输结果、运行可取消的查询以及利用复制协议。

加速Prisma ORM

上述所有内容均适用于使用TCP连接Prisma Postgres。如果您正在使用Prisma ORM,这将自动处理,并伴随着对网络往返的其他优化。事实上,我们从优化Prisma ORM和Prisma Postgres之间的连接中学到的策略,为我上面讨论的TCP改进提供了蓝图。

当Prisma ORM与Prisma Postgres配合使用时,您应用程序中的Prisma Client会利用轻量级HTTP客户端而不是完整的Postgres客户端。您发送到Prisma Postgres的Prisma操作以JSON表示形式序列化,并通过我们的Cloudflare支持的边缘网络发送,直到它到达靠近您的Postgres数据库的查询执行器。该执行器能够将JSON转换为一个或多个SQL查询,并使用其内置的连接池执行它们。这会将每次Prisma操作的地理网络调用减少到1次,无论其复杂程度如何。这种网络级优化是我们可以按Prisma操作而不是计算时间收费的方式。

我们如何才能做得更好?让他们更近!我们正在将查询执行器从基于AWS EC2的架构迁移到支持Prisma Postgres数据库的同一裸机基础设施上。复杂、深度嵌套的Prisma查询将在执行查询的同一硬件上分解为单个SQL查询。这消除了执行查询的网络延迟,极大地改变了性能配置文件。

因此,我们仍然建议使用现有方法而非TCP将Prisma ORM连接到Prisma Postgres。我们正在尽一切努力在Postgres协议的限制下优化TCP连接,但拥有完整的请求生命周期使我们能够走得更远。

总结

我在网上看到很多关于将应用程序迁移到边缘的讨论,随后是关于移回靠近数据的讨论。事实是,这取决于情况。如果您的应用程序更接近用户,响应速度会更快;如果您的应用程序离数据库更远,数据库查询速度会更慢。

您可以做的是尽可能减少与数据库的往返次数,并避免顺序、瀑布式查询。借助Prisma Postgres,我们可以在这些查询发生时最大限度地降低其成本。不要放弃边缘。

我希望这篇文章能帮助您更好地了解Postgres连接。如果您对Postgres、Prisma Postgres或Prisma ORM有更多疑问,请在XBlueskyPrisma Discord上找到我。


尝试Prisma Postgres

不要错过下一篇文章!

订阅 Prisma 新闻通讯

© . This site is unofficial and not affiliated with Prisma Data, Inc.