OpenAI: Scaling PostgreSQL to the Next Level

tr_cn 2025-05-19 22:29:18 13,388 1

PGConf.dev 2025 全球开发者大会上,来自 OpenAI 的 Bohan Zhang 分享了 OpenAI 在使用 PostgreSQL 方面的最佳实践,让我们得以一窥这家著名独角兽公司如何使用数据库。

在 OpenAI,我们采用了一种非分片架构,只有一个写入节点和多个读取节点,这表明 PostgreSQL 可以在巨大的读取负载下优雅地扩展。——PGConf.dev 2025,来自 OpenAI 的 Bohan Zhang

introduction.png

Bohan Zhang 是 OpenAI 基础设施团队的成员。他曾在卡内基梅隆大学师从 Andy Pavlo 教授,并与他共同创立了 OtterTune

背景

PostgreSQL 是支撑 OpenAI 大部分关键系统的核心数据库。如果 PostgreSQL 出现停机,OpenAI 的许多关键服务将直接受到影响。过去曾发生过几次与 PostgreSQL 相关的问题导致 ChatGPT 中断的事件。

background.png

OpenAI 在 Azure 上使用托管数据库,采用经典的 PostgreSQL 主从复制架构,没有分片。这种设置由一个主数据库和超过 40 个副本组成。对于像 OpenAI 这样拥有 5 亿活跃用户的服务来说,可扩展性是一个重要的考虑因素。

挑战

在 OpenAI 的主从 PostgreSQL 架构中,读取扩展性非常好。然而,“写入请求”已成为一个主要的瓶颈。OpenAI 在这方面实施了许多优化措施,例如尽可能卸载写入负载,并避免向主数据库添加新服务。

challenges.png

PostgreSQL 的多版本并发控制 (MVCC) 设计存在一些已知问题,包括表和索引膨胀。调整自动垃圾回收 (vacuuming) 可能很复杂,因为每个写入操作都会生成一个全新的版本,并且索引访问可能需要额外的可见性检查。这些设计方面对扩展读取副本提出了挑战:例如,增加的 Write-Ahead Logging (WAL) 可能会导致更大的复制延迟,并且随着副本数量的大幅增加,网络带宽可能会成为一个新的瓶颈。

措施

为了解决这些问题,我们从多个方面入手:

控制主数据库负载

第一项优化包括平滑主数据库上的写入峰值,以最大限度地减少其负载。例如:

此外,OpenAI 努力将尽可能多的读取请求卸载到副本。对于由于属于读写事务而无法从主数据库中删除的读取请求,需要高效率。

control-loading.png

查询优化

第二项优化侧重于查询层。由于长时间的事务会阻碍垃圾回收并消耗资源,因此配置了超时以避免长时间的“Idle in Transaction”会话,超时设置在会话、语句和客户端级别。此外,还优化了复杂的多连接查询(例如,一次连接 12 个表)。演示文稿还特别提到,使用 ORM 容易导致效率低下的查询,应谨慎使用。

query_optimization.png

解决单点故障

主数据库是单点故障;如果它出现故障,写入操作将无法进行。相反,我们有许多只读副本;如果其中一个出现故障,应用程序仍然可以从其他副本读取。事实上,许多关键请求都是只读的,因此即使主数据库出现故障,它们也可以继续从中读取。

此外,我们区分低优先级请求和高优先级请求。对于高优先级请求,OpenAI 分配专用的只读副本,以防止它们受到低优先级请求的影响。

single_point_failure.png

Schema 管理

第四个措施是仅允许在此集群上进行轻量级的 schema 更改。这意味着:

另一个提到的问题是,操作期间长时间运行的查询(>1 秒)会持续阻止 schema 更改,最终导致它们失败。解决方案是让应用程序优化或卸载这些慢查询。

结果

事故案例

OpenAI 还分享了几个遇到的问题案例研究:

功能请求

最后,Bohan 向 PostgreSQL 开发者社区提出了几个问题和功能请求:

  1. 关于索引管理:未使用的索引可能导致写入放大和额外的维护开销。OpenAI 希望删除不必要的索引,但为了最大限度地降低风险,他们建议为索引提供“禁用”功能。这将允许在永久删除索引之前监控性能指标以确保稳定性。
  2. 关于可观察性:目前,pg_stat_statements 仅提供每个查询类型的平均响应时间,缺乏对 p95 和 p99 延迟指标的直接访问。他们希望获得更多类似于直方图和百分比延迟的指标。
  3. 关于 schema 更改:他们希望 PostgreSQL 记录 schema 更改事件的历史记录,例如添加或删除列和其他 DDL 操作。
  4. 监控视图语义:他们观察到一个会话的 state = Activewait_event = ClientRead 持续了两个多小时。这表明连接在 QueryStart 后长时间保持活动状态,并且此类连接无法被 idle_in_transaction 超时终止。他们试图了解这是否是一个错误以及如何解决它。
  5. 最后,他们建议优化 PostgreSQL 的默认参数,并指出当前的默认值过于保守。他们询问是否可以实施更好的默认值或基于启发式的设置。

老冯评论

虽然 PGConf.Dev 2025 主要侧重于开发,但也经常会有用户方的用例分享,比如 OpenAI 在 PostgreSQL 方面的可扩展性实践。像这样的话题实际上对核心开发者来说非常有趣,因为他们中的许多人并不了解 PostgreSQL 在极端真实世界场景中的使用方式。

自 2017 年底以来,老冯在 Tantan 管理了数十个 PostgreSQL 集群,这是当时中国互联网领域最大、最复杂的部署之一:数十个 PostgreSQL 集群处理大约 250 万 QPS。当时,他们最大的核心集群使用了一个主节点和 33 个副本,并承载了大约 40 万 QPS。瓶颈也在于单节点写入性能,他们最终通过应用程序端的数据库和表分片来解决。

你可以说,OpenAI 在演讲中遇到的问题和应用的解决方案都是他们以前处理过的事情。当然,现在的不同之处在于,如今的顶级硬件比八年前强大得多。这使得像 OpenAI 这样的初创公司可以使用单个 PostgreSQL 集群(无需分片或分区)来服务他们的整个业务。毫无疑问,这再次有力地证明了“分布式数据库是一种虚假需求”的观点。

OpenAI 在 Azure 上使用托管的 PostgreSQL,具有顶级的服务器规格。副本数量达到 40 多个,包括一些跨区域副本。这个庞大的集群总共处理大约 100 万 QPS(读取 + 写入)。他们使用 Datadog 进行监控,他们的服务通过 Kubernetes 内的应用程序端 PgBouncer 连接池访问 RDS 集群。

由于 OpenAI 是战略级客户,因此 Azure PostgreSQL 团队提供非常积极的支持。但显然,即使拥有顶级的云数据库服务,用户仍然需要在应用程序和运维方面具备强大的意识和能力。即使拥有 OpenAI 的智慧,他们在 PostgreSQL 的运维实践中仍然会遇到陷阱。

本次演讲没有讨论高可用性,所以我们可以假设这由 Azure PostgreSQL RDS 处理。同时,监控对于系统运维至关重要。OpenAI 使用 Datadog 监控 PostgreSQL,即使拥有 OpenAI 的财力,他们仍然觉得 Datadog 贵得离谱。

会议结束后,在晚上的社交活动中,老冯与 Bohan 和另外两位数据库创始人聊到凌晨。私人谈话非常引人入胜,尽管老冯无法透露更多细节——哈哈。

social.png

老冯问答

关于 Bohan 提出的问题和功能请求,老冯在此提供一些解答。事实上,OpenAI 正在寻找的大部分功能已经在 PostgreSQL 生态系统中存在——只是可能无法在核心 PostgreSQL 或 Azure RDS 上使用。

关于禁用索引

PostgreSQL 实际上确实具有禁用索引的功能。你只需在 pg_index 系统目录中将 indisvalid 字段设置为 false 即可。这会使 planner 忽略该索引,尽管它仍会在 DML 操作期间进行维护。从技术角度来看,这完全没有问题——这与通过 isreadyisvalid 标志并发创建索引期间使用的机制相同。这不是什么黑魔法。

也就是说,OpenAI 无法使用此方法是可以理解的——RDS 不授予超级用户权限,因此你无法直接修改系统目录来实现此目的。

但回到最初的目标——避免意外删除索引——有一个更简单的解决方案:只需通过监控视图确认该索引未在主节点或副本上使用即可。如果长时间未被访问,则可以安全地删除。

使用 Pigsty 监控系统,你可以观察 PGSQL 表的实时索引切换过程。

monitoring.png

CREATE UNIQUE INDEX CONCURRENTLY pgbench_accounts_pkey2
ON pgbench_accounts USING BTREE(aid);
-- Mark the original index as invalid (won’t be used) but still maintained
UPDATE pg_index SET indisvalid = false
WHERE indexrelid = 'pgbench_accounts_pkey'::regclass;

关于可观察性

pg_stat_statements 可能不会很快提供 P95 或 P99 百分比指标,因为这将大大增加扩展的内存占用——可能是数十倍。虽然现代服务器可以处理它,但极其保守的环境可能无法处理。我向 pg_stat_statements 的维护者询问了此事,这不太可能发生。我还询问了 pgbouncer 的维护者 Jelte,短期内也不太可能实现此功能。

但这个问题可以解决。首先,pg_stat_monitor 扩展确实提供了详细的百分比延迟 (RT) 指标,并且肯定会起作用,但你需要考虑收集此类指标的性能开销。第二个选择是使用 eBPF 被动收集 RT 指标,当然,最简单的方法是直接在应用程序的数据访问层 (DAL) 中添加查询延迟监控。

最优雅的解决方案可能是基于 eBPF 的侧信道收集,但由于他们使用的是没有服务器访问权限的 Azure 托管 PostgreSQL,因此此选项可能被排除在外。

关于 Schema 更改历史记录

实际上,PostgreSQL 日志已经提供了此功能——只需将 log_statement 设置为 ddl(或更详细地设置为 modall),所有 DDL 语句都将被记录。pgaudit 扩展提供了类似的功能。

但我怀疑他们真正想要的不是日志,而是可以通过 SQL 查询的系统视图。在这种情况下,另一个选择是使用 CREATE EVENT TRIGGER 将 DDL 事件直接记录到数据表中。pg_ddl_historization 扩展提供了一种更容易的方法来执行此操作,我已经编译并打包了这个扩展。

但是,创建事件触发器也需要超级用户权限。AWS RDS 有一些特殊的处理方式可以实现这一点,但 Azure 的 PostgreSQL 似乎不支持它。

关于监控视图的语义

在 OpenAI 的示例中,State = Active 意味着后端进程仍在单个 SQL 语句的生命周期内——它尚未向前端发送 ReadyForQuery 消息,因此 PostgreSQL 仍然认为该语句“尚未完成”。因此,行锁、缓冲区引脚、快照和文件句柄等资源仍被视为“正在使用”。WaitEvent = ClientRead 意味着该进程正在等待来自客户端的输入。当两者同时出现时,一个典型的情况是空闲的 COPY FROM STDIN,但也可能是由于 TCP 阻塞或卡在 BIND 和 EXECUTE 之间。因此很难明确地说这是一个错误——这取决于连接实际在做什么。

有些人可能会争辩说,从 CPU 的角度来看,等待客户端 I/O 应该算作“空闲”。但 State 跟踪语句的执行状态,而不是该进程是否正在积极使用 CPU。查询可能处于 Active 状态,但未在 CPU 上运行(当 WaitEvent 为 NULL 时),或者它可能在 CPU 上循环等待客户端输入(即 ClientRead)。

回到核心问题——有一些方法可以解决它。例如,在 Pigsty 中,当通过 HAProxy 访问 PostgreSQL 时,主服务在负载均衡器级别设置了最大连接生存期(例如,24 小时)。在更严格的环境中,这可以短至一小时。这意味着超过生存期的连接将被终止。理想情况下,客户端连接池应该主动强制执行连接生存期,而不是被强制断开连接。对于离线、只读服务,不需要此超时——允许可能持续数天的长时间运行的查询。这种方法为连接处于 Active 但等待 I/O 的情况提供了一个安全保障。

也就是说,尚不清楚 Azure PostgreSQL 是否提供这种控制。

关于默认参数

PostgreSQL 的默认参数非常保守。例如,它默认为仅 256 MB 的内存(并且可以设置为低至 256 KB!)。这样做的好处是 PostgreSQL 几乎可以在任何环境中启动和运行。缺点是什么?我见过一个具有 1 TB 物理内存的生产设置仍在以默认的 256 MB 配置运行……(由于双重缓冲,它实际上运行了很长时间。)

总的来说,我认为保守的默认值并不是一件坏事。这个问题可以通过更灵活的动态配置来解决。像 RDS 和 Pigsty 这样的服务为初始参数调整提供了精心设计的启发式方法,这已经很好地解决了这个问题。也就是说,此功能仍然可以构建到 PostgreSQL 命令行工具中——例如,在 initdb 期间,该工具可以自动检测 CPU、内存、磁盘大小和类型,并相应地设置合理的默认值。

自托管?

OpenAI 设置中真正的挑战并非来自 PostgreSQL 本身,而是来自在 Azure 上使用托管 PostgreSQL 的限制。一个解决方案是绕过这些限制,使用 Azure 或其他云的 IaaS 层,在本地 NVMe SSD 实例上部署自托管 PostgreSQL 集群。

事实上,Pigsty 是老冯专门为解决这种规模下的 PostgreSQL 挑战而构建的——它本质上是一个自托管的 RDS 解决方案,并且可以很好地扩展。OpenAI 已经遇到或将要遇到的许多问题都已经在 Pigsty 中实现了解决方案,Pigsty 是开源且免费的。

如果 OpenAI 有兴趣,我很乐意提供一些帮助。也就是说,当一家公司像他们一样快速扩展时,调整数据库基础设施可能不是首要任务。幸运的是,他们有一些出色的 PostgreSQL DBA,他们可以继续推进并探索这些道路。

本文已获得 老冯 的授权进行翻译和转载。原文链接:https://mp.weixin.qq.com/s/ykrasJ2UeKZAMtHCmtG93Q