← 返回信息流
AI 资讯Hacker News·3 小时前

我们为何又造了一个Postgres连接池

原标题:Why we built yet another Postgres connection pooler

速览

PostgreSQL团队宣布构建另一个连接池工具,主要因为现有方案在高并发场景下存在瓶颈。新连接池优化了连接复用效率,降低延迟,并支持更灵活的扩展机制。这一举措表明Postgres持续强化其数据库基础设施能力。

AI 深度解读

背景

随着 Postgres 数据库的广泛使用,连接池技术成为了扩展数据库能力的关键组件。连接池允许大量客户端应用共享有限的数据库连接,避免因连接数超限而导致服务不可用。市面上已有多个成熟的连接池工具,如 PgBouncer、RDS Proxy、Pgpool-II、Supavisor 等。然而,这些工具在实践中存在一些“泄漏抽象”(leaky abstraction)的问题,迫使开发者修改应用代码或放弃某些 Postgres 原生功能。为了解决这一痛点,作者团队构建了一个新的 Postgres 连接池代理——PgDog。

核心内容

PgDog 是一个用于扩展 Postgres 的代理,其核心功能之一是连接池,使众多客户端应用能够共用一个数据库而不超过连接限制。与其他连接池工具不同,PgDog 旨在消除“泄漏抽象”带来的负面影响,让开发者无需修改应用代码即可保持对 Postgres 全部功能的访问。

泄漏抽象问题

大多数 Postgres 生态工具遵循 Unix 哲学:只做一件事并做到极致。PgBouncer 正是这一理念的成功典范,但它引入了泄漏抽象——部署后,开发者很快意识到这个基础设施改变了数据库的使用方式。使用 PgBouncer 或 RDS Proxy 通常需要做出权衡,常常需要修改应用代码。对于已经运行一段时间的应用来说,这可能意味着需要更改数千行生产代码,且通常缺少测试覆盖。PgDog 的设计目标就是消除这种必要性。

连接状态问题

添加连接池后,首先失去的是会话控制(Session Control),即 SET 命令。SET 用于临时覆盖数据库设置,例如执行慢查询时设置超时时间:

SET statement_timeout TO '5m';
SELECT * FROM users WHERE banned IS true;

由于连接池在客户端之间复用连接,一个客户端的连接状态会“泄漏”到另一个客户端。在生产环境中,后果可能很严重:最好情况是慢查询运行过久导致数据库事故;最坏情况是依赖会话变量的行级安全(RLS)策略失效,数据悄然消失。因此,迁移到连接池时的典型建议是“不再使用 SET”。但 SET 是 Postgres 的正经功能,禁止使用就意味着需要做出权衡并改变应用编写方式。如果依赖它实现 RLS 等关键功能,则完全无法使用连接池。

PgDog 的解决方案:处理 SET 语句

PgDog 内置 SQL 解析器,能够检测 SET 语句,提取变量名和值,并将它们存储在每个客户端连接的代理中。当客户端执行查询时,PgDog 首先检查其状态是否与服务器匹配,如果不匹配,则自动执行一系列 SET 语句来更新状态。其检测算法非常快速,如果有多个变量不同,会使用查询流水线(query pipelining)在一次往返中完成更新。这使得性能影响很小,开发者可以在扩展的同时继续使用依赖的 Postgres 功能。

LISTEN/NOTIFY 支持

LISTENNOTIFY 是 Postgres 内置的发布/订阅队列命令。这是一个非常实用的功能,无需引入额外数据库即可工作。但在使用事务模式的连接池时,此功能通常会被放弃。PgDog 在内部处理这两个命令,同时在多个 PgDog 进程之间传递消息。对客户端而言,PgDog 看起来就是消息代理,但实际上底层仍然是 Postgres。同时,PgDog 保留了 NOTIFY 的所有事务语义(包括修复了一些会导致崩溃的问题)。

内部实现上,PgDog 使用 Tokio 的广播通道(broadcast channel)在同一进程内的客户端之间传递消息。为支持多个 PgDog 进程(例如生产环境中运行多个容器),所有 LISTENNOTIFY 命令也会通过专用连接发送到 Postgres。实际上,PgDog 充当了一个发布/订阅客户端,代理其他发布/订阅客户端,将 Postgres 作为真正的消息代理。

多线程架构

PgDog 基于 Rust 异步运行时 Tokio 构建,支持多线程工作。每个客户端由独立的异步任务处理,任务数随连接数线性扩展。借助 Tokio,PgDog 能够利用多核 CPU,从单个进程服务更多客户端和每秒更多查询。

相比之下,PgBouncer 的 SO_REUSEPORT 和 RDS Proxy 的“无服务器”自动扩展都存在“分片”连接池的问题:每个代理进程拥有专属的 Postgres 连接集合,客户端一旦连接就不可更换实例。当某个代理过载时,所有连接它的客户端都会卡住。PgDog 通过多核多线程处理大量流量,能用更少的服务器连接池化更多客户端,提高连接利用率和效率。多线程进程还能更好地应对突发查询,无需等待自动扩展。如果应用有延迟 SLA,代理本身不应成为瓶颈。最后,管理多线程进程的部署手册更短:只需关注单一的指标和健康检查来源,而不必将复杂性推入难以调试的地方(如 Linux 内核或 AWS RDS 控制平面)。

关键要点

  • PgDog 旨在解决连接池的“泄漏抽象”问题:使用 PgBouncer 等工具需要修改应用代码或放弃 Postgres 原生功能(如 SETLISTEN/NOTIFY),PgDog 则让这些功能正常工作,无需修改代码。
  • 内置 SQL 解析器处理 SET 语句:自动检测、存储并恢复客户端连接状态,使用流水线技术最小化性能开销,使 RLS 等依赖会话变量的功能在连接池环境下依然可用。
  • 完整支持 LISTEN/NOTIFY:在 Postgres 内部处理发布/订阅消息,跨 PgDog 进程传递,保留事务语义,让开发者在扩展时继续使用这一原生特性。
  • 基于 Tokio 的多线程架构:利用多核 CPU 线性扩展客户端处理能力,避免单进程“分片”导致的过载问题,提升连接利用率和突发处理能力,同时简化运维复杂度。
  • 已在生产环境中运行超过一年,高峰期每秒处理 200 万次查询,开源且免费,可自由使用和修改。

意义与影响

PgDog 的出现打破了连接池领域“功能与抽象不可兼得”的固有观念。它证明了一个连接池工具完全可以既提供高性能连接复用,又完整保留 Postgres 的全部原生功能,从而极大地降低了从单节点数据库向大规模部署迁移的门槛。对开发者而言,这意味着不再需要在“使用连接池”和“使用 SETLISTEN/NOTIFY 等特性”之间做痛苦的权衡,也无需修改大量遗留代码。

在多线程架构方面,PgDog 展示了 Rust 和 Tokio 在数据库基础设施领域的潜力,为后续同类工具的设计提供了新思路。它的开源属性鼓励社区参与改进,可能推动整个 Postgres 连接池生态的演进。长远来看,类似 PgDog 这种“不妥协”的工具将帮助更多团队在享受连接池带来的扩展性收益的同时,保持应用的稳定性和开发效率,尤其适合对延迟敏感或依赖高级 Postgres 特性的生产环境。

查看原文 →pgdog.dev