扩展PostgreSQL以支持8亿ChatGPT用户

OpenAI Blog 新闻

摘要

OpenAI分享了扩展PostgreSQL以支持8亿ChatGPT用户及每秒数百万查询的技术见解,采用了单主架构搭配50个只读副本,同时通过分片和优化策略管理写入密集型工作负载带来的挑战。

深入了解OpenAI如何通过副本、缓存、速率限制和工作负载隔离,将PostgreSQL扩展至每秒处理数百万查询。
查看原文
查看缓存全文

缓存时间: 2026/04/20 14:52

# 规模化 PostgreSQL 以支撑 8 亿 ChatGPT 用户 多年来,PostgreSQL 一直是支撑 ChatGPT 和 OpenAI API 等核心产品最关键、最底层的数据库系统之一。随着用户规模的快速增长,我们数据库所承受的压力也在指数级上升。过去一年,PostgreSQL 的负载增长了 10 倍以上,并且仍在快速攀升。 为了应对这种增长,我们在推进生产基础设施时获得了一个新认知:PostgreSQL 可以规模化到可靠地支撑远超以往人们想象的大规模读密集型工作负载。这个最初由加州大学伯克利分校的科学家团队创建的系统,使我们能够依靠单个主节点 Azure PostgreSQL 灵活服务器实例[(在新窗口中打开)](https://learn.microsoft.com/en-us/azure/postgresql/overview)和分布在多个区域的近 50 个只读副本,来支撑巨大的全球流量。本文将讲述 OpenAI 如何通过严格的优化和扎实的工程,将 PostgreSQL 扩展到每秒处理数百万次查询,为 8 亿用户提供服务;我们还将分享一路走来学到的关键经验。 ChatGPT 上线后,流量以前所未有的速度增长。为了支撑它,我们迅速在应用层和 PostgreSQL 数据库层实施了大量优化:通过增大实例规模纵向扩展,并通过增加更多只读副本横向扩展。这种架构长期运作良好,持续优化后,它为未来的增长提供了充足的扩展空间。 听到单一主节点架构能满足 OpenAI 规模的需求,可能会让人惊讶;然而,要让它真正工作并非易事。我们曾多次遇到因 Postgres 过载导致的严重事件 (SEV),它们的模式往往相似:上游问题导致数据库负载突然飙升,例如缓存层故障引发大量缓存未命中、大量昂贵的多表连接耗尽 CPU、或新功能上线导致写入风暴。随着资源利用率攀升,查询延迟增加,请求开始超时。重试进一步放大了负载,引发恶性循环,可能使整个 ChatGPT 和 API 服务降级。 虽然 PostgreSQL 在我们的读密集型工作负载下扩展良好,但在高写入流量时段仍面临挑战。这主要归咎于 PostgreSQL 的多版本并发控制 (MVCC) 实现,它在写密集型工作负载下效率较低。例如,当查询更新一个元组甚至单个字段时,整行都会被复制以创建新版本。在重写负载下,这会导致显著的写放大。它还会增加读放大,因为查询必须扫描多个元组版本(死元组)才能获取最新版本。MVCC 还带来了表膨胀和索引膨胀、索引维护开销增加以及复杂的自动清理 (autovacuum) 调优等挑战。(你可以在我和卡内基梅隆大学的 Andy Pavlo 教授合著的一篇博客中找到对这些问题的深入探讨,博客名为《PostgreSQL 中最让我们讨厌的部分》[(在新窗口中打开)](https://www.cs.cmu.edu/~pavlo/blog/2023/04/the-part-of-postgresql-we-hate-the-most.html),该博客被引用在 PostgreSQL 维基百科页面[(在新窗口中打开)](https://en.wikipedia.org/wiki/PostgreSQL#cite_note-37)中。) 为了缓解这些限制并减少写入压力,我们已经并将继续把可分片(即可水平分区)的写密集型工作负载迁移到分片系统,如 Azure Cosmos DB,并优化应用逻辑以减少不必要的写入。我们也不再允许在当前的 PostgreSQL 部署中添加新表。新的工作负载默认使用分片系统。 即使基础设施在演变,PostgreSQL 仍然保持不分片,由单个主节点实例处理所有写入。主要原因是,对现有应用工作负载进行分片将极其复杂且耗时,需要修改数百个应用端点,可能需要数月甚至数年。由于我们的工作负载主要是读密集型,并且我们已经实施了大量优化,当前的架构仍然为支持持续的流量增长提供了充足的余量。虽然我们不排除将来对 PostgreSQL 进行分片,但鉴于当前和未来增长有足够的扩展空间,这不是近期优先事项。 在接下来的部分中,我们将深入探讨我们面临的挑战以及为应对这些挑战、防止未来服务中断所实施的广泛优化,从而将 PostgreSQL 推向极限,将其扩展到每秒数百万次查询。 **挑战:** 由于只有一个写入节点,单主节点设置无法扩展写入能力。剧烈的写入峰值可能迅速压垮主节点,影响 ChatGPT 和 API 等服务。 **解决方案:** 我们尽可能减小主节点上的负载——包括读取和写入——以确保它有足够的容量来处理写入峰值。读取流量尽可能卸载到副本上。然而,某些读取查询必须留在主节点上,因为它们是写入事务的一部分。对于这些查询,我们专注于确保它们高效,避免慢查询。对于写入流量,我们已经将可分片的写密集型工作负载迁移到分片系统,如 Azure CosmosDB。难以分片但写入量仍然很高的工作负载迁移需要更长时间,这个过程仍在进行中。我们还积极优化应用程序以减少写入负载;例如,我们修复了导致冗余写入的应用错误,并在适当的地方引入了惰性写入,以平滑流量峰值。此外,在回填表字段时,我们强制执行严格的速率限制,以防止过度的写入压力。 **挑战:** 我们在 PostgreSQL 中发现了几个昂贵的查询。过去,这些查询的突然量爆发会消耗大量 CPU,拖慢 ChatGPT 和 API 请求。 **解决方案:** 几个昂贵的查询,例如连接多个表的查询,可能会显著降低甚至拖垮整个服务。我们需要持续优化 PostgreSQL 查询,以确保它们高效并避免常见的在线事务处理 (OLTP) 反模式。例如,我们曾发现一个极其昂贵的查询,它连接了 12 个表,该查询的峰值是过去高严重性 SEV 的原因。应尽可能避免复杂的多表连接。如果连接是必要的,我们学会了考虑分解查询,将复杂的连接逻辑移到应用层。许多有问题的查询是由对象关系映射框架 (ORM) 生成的,因此仔细审查它们生成的 SQL 并确保其行为符合预期非常重要。在 PostgreSQL 中也很常见长时间空闲的查询。配置像 idle_in_transaction_session_timeout 这样的超时对于防止它们阻塞自动清理至关重要。 **挑战:** 如果只读副本宕机,流量仍可路由到其他副本。然而,依赖单个写入节点意味着存在单点故障——如果它宕机,整个服务都会受影响。 **解决方案:** 大多数关键请求只涉及读取查询。为了减轻主节点的单点故障,我们将这些读取从写入节点卸载到副本,确保即使主节点宕机,这些请求也能继续服务。虽然写入操作仍然会失败,但影响已减小;由于读取仍可用,不再是 SEV0。 为了缓解主节点故障,我们以高可用性 (HA) 模式运行主节点,配备热备节点——一个持续同步的副本,随时准备接管流量。如果主节点宕机或需要下线维护,我们可以快速提升备节点以最小化停机时间。Azure PostgreSQL 团队做了大量工作,确保即使在极高负载下,这些故障转移也能安全可靠地进行。为了处理只读副本故障,我们在每个区域部署了多个具有足够容量余量的副本,确保单个副本故障不会导致区域中断。 **挑战:** 我们经常遇到某些请求在 PostgreSQL 实例上消耗不成比例的资源的情况。这可能导致同一实例上其他工作负载的性能下降。例如,新功能上线可能引入低效的查询,严重消耗 PostgreSQL CPU,拖慢其他关键功能的请求。 **解决方案:** 为了缓解“嘈杂邻居”问题,我们将工作负载隔离到专用实例上,确保资源密集型请求的突然峰值不会影响其他流量。具体来说,我们将请求分为低优先级和高优先级层,并将它们路由到不同的实例。这样,即使低优先级工作负载变得资源密集,也不会降低高优先级请求的性能。我们对不同的产品和服务也采用同样的策略,以便一个产品的活动不会影响另一个产品的性能或可靠性。 **挑战:** 每个实例都有最大连接数限制(Azure PostgreSQL 中为 5000)。很容易耗尽连接或积累太多空闲连接。我们之前曾因连接风暴耗尽所有可用连接而导致事故。 **解决方案:** 我们部署了 PgBouncer 作为代理层来池化数据库连接。以语句或事务池模式运行它,可以高效地复用连接,大大减少活跃客户端连接数。这也降低了连接建立延迟:在我们的基准测试中,平均连接时间从 50 毫秒 (ms) 降至 5 毫秒。跨区域连接和请求开销较高,因此我们将代理、客户端和副本部署在同一区域,以最小化网络开销和连接使用时间。此外,PgBouncer 必须仔细配置。像空闲超时这样的设置对于防止连接耗尽至关重要。 每个只读副本都有自己的 Kubernetes 部署,运行多个 PgBouncer Pod。我们在同一个 Kubernetes Service 后面运行多个 Kubernetes 部署,该 Service 将流量负载均衡到各个 Pod。 **挑战:** 缓存未命中的突然峰值可能触发对 PostgreSQL 数据库的读取激增,耗尽 CPU 并拖慢用户请求。 **解决方案:** 为了减少对 PostgreSQL 的读取压力,我们使用缓存层来服务大部分读取流量。然而,当缓存命中率意外下降时,缓存未命中的爆发会将大量请求直接推送到 PostgreSQL。这种数据库读取的突然增加会消耗大量资源,拖慢服务。为了防止在缓存未命中风暴期间过载,我们实现了缓存锁定(和租约)机制,这样只有对特定键未命中的单个读取者会从 PostgreSQL 获取数据。当多个请求对同一个缓存键未命中时,只有一个请求获取锁并继续检索数据和重新填充缓存。所有其他请求等待缓存更新,而不是同时冲击 PostgreSQL。这显著减少了冗余的数据库读取,并保护系统免受级联负载峰值的影响。 **挑战:** 主节点将预写日志 (WAL) 数据流式传输到每个只读副本。随着副本数量增加,主节点必须将 WAL 发送到更多实例,增加了网络带宽和 CPU 的压力。这会导致更高且更不稳定的副本延迟,使系统更难可靠地扩展。 **解决方案:** 我们运营着近 50 个跨多个地理区域的只读副本,以最小化延迟。然而,在当前架构下,主节点必须将 WAL 流式传输到每个副本。虽然目前使用非常大的实例类型和高网络带宽可以很好地扩展,但我们不能无限地添加副本,否则最终会压垮主节点。为了解决这个问题,我们正在与 Azure PostgreSQL 团队合作开发级联复制[(在新窗口中打开)](https://www.postgresql.org/docs/current/warm-standby.html#CASCADING-REPLICATION),其中中间副本将 WAL 中继到下游副本。这种方法使我们能够扩展到可能超过一百个副本,而不会压垮主节点。然而,它也引入了额外的操作复杂性,特别是在故障转移管理方面。该功能仍在测试中;我们将确保其健壮性,并能安全地进行故障转移,然后才推广到生产环境。 **挑战:** 特定端点的流量突然激增、昂贵查询的爆发或重试风暴,可能迅速耗尽 CPU、I/O 和连接等关键资源,导致广泛的服务降级。 **解决方案:** 我们在多个层面——应用层、连接池层、代理层和查询层——实施了速率限制,以防止突然的流量峰值压垮数据库实例并引发级联故障。避免过短的重试间隔也至关重要,这可能会触发重试风暴。我们还增强了 ORM 层以支持速率限制,并在必要时完全阻止特定的查询摘要 (query digests)。这种有针对性的负载卸载 (load shedding) 可以实现对昂贵查询突然激增的快速恢复。 **挑战:** 即使是一个小的 schema 变更,例如更改列类型,也可能触发完整的表重写[(在新窗口中打开)](https://www.crunchydata.com/blog/when-does-alter-table-require-a-rewrite)。因此,我们谨慎地应用 schema 变更——限制为轻量级操作,避免任何重写整个表的变化。 **解决方案:** 只允许轻量级的 schema 变更,例如添加或删除某些不会触发完整表重写的列。我们对 schema 变更强制执行严格的 5 秒超时。允许并发创建和删除索引。Schema 变更仅限于现有表。如果新功能需要额外的表,它们必须放在替代的分片系统(如 Azure CosmosDB)中,而不是 PostgreSQL。当回填表字段时,我们应用严格的速率限制以防止写入峰值。虽然这个过程有时可能需要超过一周,但它确保了稳定性,避免对生产产生影响。 这项工作表明,通过正确的设计和优化,Azure PostgreSQL 可以扩展到处理最大的生产工作负载。PostgreSQL 以每秒数百万次查询处理读密集型工作负载,为 OpenAI 最核心的产品(如 ChatGPT 和 API 平台)提供动力。我们增加了近 50 个只读副本,同时将复制延迟保持接近零,在跨地理分布的区域维持了低延迟读取,并建立了足够的容量余量以支持未来的增长。 这种扩展是在最小化延迟和提高可靠性的同时实现的。我们持续实现了个位数毫秒级的低延迟 p99 客户端延迟,以及生产环境的 99.999% (五个九)可用性。而在过去 12 个月中,我们只发生了一次 SEV-0 PostgreSQL 事件(发生在 ChatGPT ImageGen 病毒式上线[(在新窗口中打开)](https://newsletter.pragmaticengineer.com/p/chatgpt-images) 期间,当时写入流量突然激增 10 倍以上,一周内有超过 1 亿新用户注册)。 虽然我们对 PostgreSQL 迄今为止取得的成果感到满意,但我们仍在不断突破其极限,以确保为未来增长提供足够的扩展空间。我们已经将可分片的写密集型工作负载迁移到像 CosmosDB 这样的分片系统。剩余的写密集型工作负载更具挑战性,迁移工作仍在进行中。

相似文章

大规模并行 Postgres 备份

Hacker News Top

PlanetScale 描述了它如何通过为每个分片启动 EC2 实例、从对象存储恢复之前的备份并重放 WAL,来对分片 Postgres 数据库执行大规模并行备份,从而实现超过 50 GB/s 的 PB 级备份速度。

解读 ChatGPT Work:面向十亿用户的智能体(18分钟阅读)

TLDR AI

OpenAI 发布了 ChatGPT Work,这是一款面向知识工作的 AI 智能体,集成了 Slack、电子邮件、Drive 等工具,将云端智能体带给十亿用户。文章解读了其设计、在 OpenAI 产品线中的定位,以及计划在年底前与 ChatGPT 合并的规划。

让Postgres队列实现可扩展性

Hacker News Top

一篇详细的技术博文,解释如何使用SKIP LOCKED和适当的事务隔离级别来扩展基于PostgreSQL的队列,实现每秒3万次工作流执行。