目 录CONTENT

文章目录

将 PostgreSQL 扩展至支持 8 亿 ChatGPT 用户

Administrator
2026-02-05 / 0 评论 / 0 点赞 / 0 阅读 / 0 字

📢 转载信息

原文链接:https://openai.com/index/scaling-postgresql

原文作者:Bohan Zhang,技术团队成员


多年来,PostgreSQL 始终是支撑 ChatGPT 与 OpenAI API 等核心产品的关键底层数据系统。随着用户基数的迅猛增长,数据库面临的需求也呈指数级攀升。过去一年中,我们的 PostgreSQL 负载增长了逾 10 倍,且增势不减。

在不断升级生产基础设施以支持业务增长的过程中,我们有一项关键发现:PostgreSQL 支撑读密集型负载的潜力远超想象,其扩展能力比许多人预想的更为强大。这个最初由加州大学伯克利分校团队创建的系统,使我们能够凭借单个 Azure PostgreSQL 灵活服务器主实例(在新窗口中打开),以及遍布全球多个区域的近 50 个只读副本,成功应对海量的全球流量。本文将讲述 OpenAI 技术团队如何通过严谨的优化和坚实的工程实践,将 PostgreSQL 扩展至每秒处理数百万次查询、服务 8 亿用户,并分享这一过程中积累的关键经验。

初始架构的隐忧

ChatGPT 发布后,流量以前所未有的速度激增。为此,我们在应用层与 PostgreSQL 数据库层快速实施了全面优化,既通过提升实例规格进行纵向扩展,也通过增加只读副本来横向扩展。这套架构在很长一段时间内都运行良好。持续的改进也为其未来的增长预留了充足空间。

单主架构竟能满足 OpenAI 的规模需求,或许令人意外,但其实际落地绝非易事。我们曾遭遇多次因 PostgreSQL 过载引发的严重事件(SEV),其模式往往相似:上游问题(如缓存层故障导致的大范围缓存穿透、消耗大量 CPU 的复杂多表连接查询激增,或新功能上线引发的“写入风暴”)导致数据库负载骤增。随着资源利用率攀升,查询延迟上升,请求开始超时。后续涌入的重试流量会进一步推高负载,这可能引发一个自我强化的恶性循环,最终导致 ChatGPT 与 API 服务性能严重劣化。

负载扩展示意图

尽管 PostgreSQL 对我们的读密集型工作负载扩展性良好,但在高写入流量期间,我们仍面临挑战。这很大程度上源于 PostgreSQL 的多版本并发控制(MVCC)实现机制,使其在处理写入密集型负载时效率较低。例如,当一个查询更新一个元组甚至单个字段时,整行数据都会被复制以创建新版本。在高写入负载下,这会导致显著的写入放大。同时,由于查询必须扫描多个元组版本(死元组)以获取最新数据,这也增加了读取放大。MVCC 还带来了表与索引膨胀、索引维护开销增加以及自动清理调优复杂等额外挑战。(关于这些问题的深入探讨,可参考我与卡内基梅隆大学 Andy Pavlo 教授合著的博客《我们最讨厌的 PostgreSQL 部分》(在新窗口中打开),该文已被 PostgreSQL 维基百科页面引用⁠(在新窗口中打开)が通販できます。)

为缓解上述限制、减轻写入压力,我们已经并将继续把可分片的写入密集型工作负载(即可水平分区的工作负载)迁移至 Azure Cosmos DB 等分片系统,并优化应用逻辑以尽可能减少不必要的写入。同时,我们也不再允许在当前的 PostgreSQL 部署中添加新表。新的工作负载默认使用分片系统。

即便基础设施持续演进,PostgreSQL 仍保持未分片状态,由单个主实例处理所有写入。主要考量在于,对现有应用工作负载进行分片将极其复杂且耗时,需修改数百个应用端点,可能耗费数月甚至数年。鉴于我们的工作负载以读为主,且已实施大量优化,当前架构仍有充足余量支持流量持续增长。虽然不排除未来对 PostgreSQL 进行分片的可能性,但考虑到当前及未来的增长空间充裕,这并非近期优先事项。

下文将深入探讨我们面临的挑战,以及为防止未来服务中断、将 PostgreSQL 推向极限并扩展至每秒数百万次查询(QPS)所实施的一系列深度优化。

挑战:单一写入节点意味着单主架构无法横向扩展写入能力。剧烈的写入峰值可能迅速压垮主库,进而影响 ChatGPT 及 API 等服务。

解决方案:我们尽最大努力减少主库负载(包括读和写),以确保其有足够容量应对写入峰值。读取流量尽可能被分流至副本。然而,部分读查询因其属于写事务的一部分而必须留在主库执行,对此我们着力确保其高效并避免慢查询。对于写入流量,我们已将可分片的写入密集型负载迁移至 Azure Cosmos DB 等分片系统。更难分片但仍产生高写入量的负载迁移耗时更长,目前仍在进行中。我们还积极优化应用以减少写入负载,例如:修复导致冗余写入的应用 Bug;在适当场景引入延迟写入以平滑流量峰值。此外,在回填表字段时,我们会强制执行严格的速率限制,以防产生过度的写入压力。

挑战:我们识别出 PostgreSQL 中一些高开销查询。过去,这类查询的流量一旦激增,便会大量挤占 CPU 资源,导致 ChatGPT 和 API 服务出现延迟。

解决方案:少数几个高开销查询(例如涉及多表连接的查询)就可能导致整个服务严重劣化甚至中断。我们需要持续优化 PostgreSQL 查询,确保其高效并避免常见的 OLTP 反模式。例如,我们曾定位到一个连接了 12 张表的极高开销查询,其流量峰值是过去几次高严重性 SEV 事件的元凶。我们应尽可能避免复杂的多表连接。如果必须连接,我们学会考虑将查询拆分,并将复杂的连接逻辑移至应用层处理。许多有问题的查询由对象关系映射(ORM)框架生成,因此仔细审查其生成的 SQL 并确保其符合预期至关重要。PostgreSQL 中也常出现长时间运行的空闲查询,配置 idle_in_transaction_session_timeout 等超时参数对于防止其阻塞自动清理至关重要。

挑战:若某个读副本故障,流量可被路由至其他副本。但依赖单一写入节点意味着存在单点故障 — 一旦它宕机,整个服务将受影响。

解决方案:大多数关键请求仅涉及读查询。为缓解主库的单点故障风险,我们将这些读查询从主库卸载至副本,确保即使主库宕机,这些请求仍可继续服务。虽然写操作会失败,但影响面已缩小,不再构成最高级别(SEV-0)事故。

为减轻主库故障影响,我们以高可用(HA)模式运行主库,并配备一个热备副本(持续同步、随时可接管流量的副本)。当主库宕机或需下线维护时,我们能快速提升备用副本,最大限度减少停机时间。Azure PostgreSQL 团队已做了大量工作,确保即使在极高负载下,这些故障转移也能安全可靠。为应对读副本故障,我们在每个区域部署多个副本,并预留充足的容量余量,确保单个副本故障不会导致区域性服务中断。

挑战:我们常遇到某些请求在 PostgreSQL 实例上消耗不成比例资源的情况,导致同一实例上其他工作负载性能下降。例如,新功能上线可能引入低效查询,大量消耗 PostgreSQL CPU,拖慢其他关键功能的请求。

解决方案:为缓解“吵闹的邻居”问题,我们将不同工作负载隔离至专用实例,确保资源密集型请求的突发流量不会干扰其他业务。具体而言,我们将请求划分为低优先级与高优先级层级,并将其路由至不同的实例。如此一来,即使低优先级工作负载变得资源密集,也不会影响高优先级请求的性能。我们将此策略同样应用于不同产品与服务之间,确保单一产品的活动不会影响其他产品的性能与可靠性。

挑战:每个实例有最大连接数限制(Azure PostgreSQL 中为 5000)。很容易耗尽连接或积累过多空闲连接。我们之前曾发生过因连接风暴耗尽所有可用连接的事件。

解决方案:我们部署 PgBouncer 作为代理层来实现数据库连接池。使其运行在语句或事务池模式下,能高效复用连接,极大减少了活跃的客户端连接数。这也降低了连接建立延迟:基准测试显示,平均连接时间从 50 毫秒降至 5 毫秒。跨区域连接和请求成本高昂,因此我们将代理、客户端及副本同区域共置,以尽可能减少网络开销和连接占用时间。此外,必须谨慎配置 PgBouncer,空闲超时等设置对于防止连接耗尽是非常重要的。

挑战:缓存命中率的突然暴跌会引发 PostgreSQL 数据库的读取请求激增,导致 CPU 饱和,从而拖慢用户请求。

解决方案:为减轻 PostgreSQL 的读取压力,我们使用缓存层来处理大部分读流量。然而,当缓存命中率意外下降时,缓存未命中的洪流可能将大量请求直接推至 PostgreSQL。这种读取量的突然激增会消耗大量资源,拖慢服务。为防止在“缓存未命中风暴”期间发生过载,我们实现了缓存锁定(及租赁)机制:针对特定缓存键,只允许单个未命中的请求前往 PostgreSQL 获取数据。当多个请求同时未命中同一缓存键时,只有一个请求能获得锁,进而检索数据并回填缓存,其他所有请求则等待缓存更新,而非同时涌向数据库。这显著减少了冗余的数据库读取,保护系统免受级联负载峰值冲击。

挑战:主库需将预写日志(WAL)数据流式传输至每个读副本。随着副本数量增加,主库需向更多实例发送 WAL,这对网络带宽和 CPU 都造成额外压力,导致副本延迟更高且更不稳定,使系统难以可靠扩展。

解决方案:我们在全球多个地理区域运行了近 50 个读副本以尽可能减小延迟。然而在当前架构下,主库必须向每个副本流式传输 WAL。尽管目前通过使用超大型实例类型和高网络带宽尚能良好扩展,但我们无法无限制地添加副本而不最终压垮主库。为此,我们正与 Azure PostgreSQL 团队合作开发级联复制方案(在新窗口中打开),即由中间副本将 WAL 中继给下游副本。此方法使我们有望扩展到上百个副本,而不会令主库不堪重负。但这也会引入额外的运维复杂性,尤其是在故障转移管理方面。该功能仍在测试中;我们将在确保其健壮且能安全故障转移后,再将其推向生产环境。

PostgreSQL 级联复制示意图

速率限制

挑战:特定端点的突发流量、高开销查询激增或重试风暴,都可能迅速耗尽 CPU、I/O 和连接等关键资源,引发大面积服务降级。

解决方案:我们在应用层、连接池、代理和查询层实施了多层速率限制,以防止突发流量压垮数据库实例并引发级联故障。同样关键的是避免过短的重试间隔,以防触发重试风暴。我们还增强了 ORM 层,以支持速率限制,并在必要时完全阻断特定查询摘要。这种有针对性的负载卸载能力,使我们能从高开销查询的突然激增中快速恢复。

模式管理

挑战:即便是很小的模式变更(例如更改列类型),也可能触发全表重写⁠(在新窗口中打开)。因此,我们对模式变更格外谨慎,仅限执行轻量级操作,避免任何会导致全表重写的改动。

解决方案:仅允许进行轻量级的模式变更,例如添加或删除某些不会触发全表重写的列。我们严格执行 5 秒超时限制。允许并发创建和删除索引。模式变更仅限于现有表。若新功能需要新增表,则必须创建在 Azure Cosmos DB 等替代分片系统中,而非 PostgreSQL。在回填表字段时,我们会应用严格的速率限制,以防止写入峰值。尽管此过程有时可能超过一周,但它确保了稳定性,避免了任何生产影响。

本次实践表明,凭借恰当的设计与优化,Azure PostgreSQL 完全能够扩展以应对超大规模的生产负载。如今,PostgreSQL 每秒为读密集型工作负载处理数百万次查询,支撑着 ChatGPT 和 API 平台等 OpenAI 的核心产品。我们成功增添了近 50 个读副本,同时将复制延迟维持在趋近于零,保障了跨地理分布区域的低延迟读取,并构建了足以支撑未来增长的充足容量空间。

这一切扩展是在持续优化延迟与提升可靠性下实现的。我们在生产环境中持续交付低两位数毫秒的 P99 客户端延迟与 99.999%(五个九)的可用性。在过去 12 个月中,我们仅发生过一次 SEV-0 级别的 PostgreSQL 事件(发生在 ChatGPT ImageGen 病毒式发布⁠(在新窗口中打开)期间,一周内超过 1 亿新用户注册,导致写入流量骤增 10 倍以上)。

虽然我们对 PostgreSQL 已取得的成就感到满意,但我们仍在不断挑战其极限,以确保为未来增长留有足够空间。我们已经将可分片的写入密集型工作负载迁移至 Cosmos DB 等分片系统。剩余的写入密集型负载更难分片 — 我们也在积极迁移这些负载,以进一步减轻 PostgreSQL 主库的写入压力。我们也在与 Azure 合作启用级联复制,以便能够安全地扩展到更多的读副本。

展望未来,随着基础设施需求的持续增长,我们将继续探索包括 PostgreSQL 分片或替代性分布式系统在内的更多扩展途径。




🚀 想要体验更好更全面的AI调用?

欢迎使用青云聚合API,约为官网价格的十分之一,支持300+全球最新模型,以及全球各种生图生视频模型,无需翻墙高速稳定,文档丰富,小白也可以简单操作。

0

评论区