← 返回 2026-09-14 简报

快速扩展在线存储规模,以支撑超10亿ChatGPT用户

语音播报
摘要
事件:OpenAI工程师详解Habitat平台演进历程,该在线存储系统已支撑超10亿周活用户。 要点:从Python客户端库重构为独立服务以解耦逻辑;引入Rust优化数据库层与Azure Cosmos DB交互; 影响:展示超大规模下存储架构的快速扩展路径,为开发者提供处理高并发。

2026年9月11日

工程实践

快速扩展在线存储以服务于超过10亿ChatGPT用户

我们如何将应用存储平台Habitat从Python改造以适应前所未有的增长。

作者:Jon Lee、Chaomin Yu 和 Ben Ries,技术团队成员

收听文章
17:57
分享

什么是Habitat?

构建服务以更好地支持多个复杂产品

大规模部署Python服务

追踪asyncio延迟

降低功能标志配置中的尾部延迟

平衡负载与管理连接池

避免淹没下游资源

为什么Habitat做得更少

从Python迁移到Rust

优化我们的数据库层Azure Cosmos DB

每个OpenAI产品都依赖于快速、可靠的数据访问,无论是用户登录、检查Codex设置,还是在ChatGPT中开始新对话。每一次操作在产品响应之前可能需要多次单独的数据查询。如果这些请求缓慢,产品就会显得迟缓;如果这些请求失败,产品将完全无法工作。

Habitat是我们构建的在线存储平台,旨在让OpenAI产品能够快速且可靠地访问所需信息。Habitat目前每秒处理超过7000万次请求,支持每周被超过10亿人使用的产品,覆盖近40个地理区域。Habitat最初于2023年DevDay发布,用于支持GPTs,起初是一个连接到单一数据库的简单Python客户端库。如今,它已成为一个复杂的分布式系统,服务于超过500PB的数据。

图01 · 什么是Habitat?

在线存储平台

Habitat是我们构建的在线存储平台,旨在让OpenAI产品能够快速且可靠地访问所需信息。

暂停
请求
响应
变更(CDC)
客户端
在线存储平台
存储资源
ChatGPT
API
Codex
内部服务
以及更多
Habitat
缓存
缓存
ACL策略
授权
放置与数据驻留
数据驻留
加密
数据安全
隔离
多租户
速率限制
请求整形
路由
模式查找 · 数据驻留
Azure Cosmos DB
在线存储
Nanobase
在线存储
Valkey
缓存
Blob存储
存储资源
CDC服务
变更数据捕获
Databricks
Rockset
Kafka
以及更多

以这种规模构建和运营基础设施并非易事,但也并非特别具有挑战性。使我们的情况独特的是,我们必须以前所未有的速度进行扩展,以支持惊人的用户增长和产品需求,同时构建一个成熟的平台。通常,系统工程师会为10倍规模的扩展做准备,并希望它能维持几年,同时为下一个10倍增长做准备。而在我们的案例中,过去三年我们每年的增长都超过了10倍。因此,构建和运营Habitat是一系列战术决策和排序的过程:在最低层面理解每个组件,以从现有栈中榨取尽可能多的性能,同时抵御存储和计算容量的瓶颈,为基础性投资争取时间。

7000万+

每秒请求数

10亿+

每周用户数

500 PB+

数据

随着OpenAI的成长,Habitat也必须随之成长:首先变得足够可靠以支持关键产品流量,然后足够快速以满足全球用户的需求,最后,能够灵活地在巨大规模下运营。这篇文章是两篇系列文章的第一篇,介绍我们如何扩展在线存储。在本文中,我们将分享Habitat的演变过程、我们为何将其从库转变为服务,以及如何将用一种不常见的服务栈语言——Python——编写的服务拉伸为一个可靠的存储平台层。

在未来的文章中,我们将详细介绍如何在大规模下实现多租户可靠性、我们优化读取性能的分层策略,以及我们如何扩展与Azure Cosmos DB的合作以可靠地应对前所未有的需求。

什么是Habitat?

Habitat 源于一个简单的理念:产品工程师无需关注数据库管理。Habitat 最初在 DevDay 2023 上发布,旨在支持 GPTs,当时它是一个与 ChatGPT 主服务器交互的小型 Python 库。它支持一组有限的操作,这些操作在底层映射到数据库应用程序 Azure Cosmos DB。

该库的任务是为产品团队提供一种简单的方法来存储和检索数据,而无需掌握底层细节。Habitat 负责处理必要的工作:确定涉及的数据类型、数据来源(或去向)、请求是否被允许等。

产品工程师无需关心模式查找、路由、授权、加密、序列化、请求整形以及连接池。他们甚至不需要考虑数据来源:是 Azure Cosmos DB、缓存还是其他类型的存储。

图 02 · HABITAT 服务

简化的 Habitat 请求流程

暂停
请求
响应
客户端
OPENAI
AZURE COSMOS DB
Habitat 客户端 SDK
envoy
habitat-service
进程 1
habitat-service
进程 2
habitat-service
进程 3
habitat-envoy
habitat-cosmos-db-us0
habitat-cosmos-db-us1
habitat-cosmos-db-eu0

这个 Python 库运行良好,尽管没有集中推动放弃使用自助式 Postgres 和 Azure Cosmos DB,Habitat 仍在 OpenAI 的产品工程师中迅速普及。

随着产品需求的发展,产品开发人员甚至很容易为共享库添加对客户端缓存、压缩或加密等功能的支持。

构建服务以更好地支持多个复杂产品

到 2025 年年中,作为客户端实现,Habitat 已触及瓶颈。随着 Habitat 层变得更加复杂以及 OpenAI 服务数量的增加,向后兼容的协议变更变得不可行。

在一次实例中,我们希望通过将最关键的数据集迁移到一组区域分布式的 Azure Cosmos DB 账户,来减少任何单个区域故障的影响范围。做出这一更改需要在客户端引入额外的路由逻辑,该逻辑通过功能标志禁用,确保其部署到所有客户端,然后启用该功能标志。

协调数十个服务的部署并与每个团队合作进行部署花费了数天时间。在启用之前,我们意识到需要引入一些影子测试(shadowing),以确保分片逻辑正确无误。这又花了几天时间进行部署。修复我们发现有误的 bug?又花了几天时间。最终,我们准备启用该标志,但其中一个团队由于无关原因将其服务回滚到之前有 bug 的客户端版本,导致了我们极力避免的故障。

对客户端库的更改需要在数十个服务之间进行复杂的协调,这一过程被证明日益脆弱、低效且容易受到操作故障的影响。为了减少未来部署的操作扩散,我们决定将 Habitat 整合为独立的服务。

通过将存储逻辑解耦为独立服务,我们为部署、可观测性和平台增强建立了单一的控制点。与其管理碎片化的更新,我们可以集中实施改进,从而为所有 OpenAI 产品提供即时收益。

集中式服务还为我们提供了单一的瓶颈点,以提供最强大的数据安全和隐私原语。Habitat 服务是我们集中执行访问控制策略、执行审计日志记录以及限制对 Azure Cosmos DB 等底层存储资源访问的地方。Habitat 在保护用户数据和防止来自外部、内部以及智能体行为者的未授权访问方面发挥着关键作用。

大规模部署 Python 服务

我们深知需要一项服务,但即便作为服务会带来 Python 的额外开销,我们也并不想立即迁移出 Python。与本地库执行相比,使用 Python 构建高吞吐量服务会增加网络延迟,并带来显著的 CPU 和内存扩展成本。此外,我们认识到在 100 倍规模下,Python 的低效性是不可接受的,最终的重写几乎不可避免。

然而,我们将此视为技术债务的战略入侵。当时我们的主要目标并非成本或资源优化,而是解除产品开发的阻塞并实现平台稳定性。通过接受 Python 服务在短期的性能折衷,我们能够优先处理更紧迫的挑战,建立核心 API,并构建稳健的基础设施。

我们还经过计算后押注,我们自身编码模型的快速进步将使未来的技术路径简化。我们赌的是,当需要完全迁移出 Python 时,Codex 和 GPT 将使这种迁移成为可能。这一赌注最终被证明是正确的。

以 Python 服务运行 Habitat 在性能方面并非最优,但却是必要的选择。Python 让我们能够快速行动,但这并不意味着我们可以抛却谨慎,接受明显更差的延迟。当平均用户请求导致数百次数据库调用时,最慢的那次数据库调用就是用户感知到的延迟。我们发现,在这个规模下运行 Python 服务的主要挑战在于管理这些尾部延迟。

追踪 asyncio 延迟

Asyncio 有助于 Python 并发执行 I/O 密集型工作负载,但无助于绕过 Python GIL(全局解释器锁)并提供 CPU 并行性。除了繁重的 I/O 请求代理外,Habitat 还处理许多 CPU 密集型职责和后台任务:路由、压缩、加密、校验和计算、下游健康检查、请求影子测试(shadowing)以及对冲(hedging)。

由于服务中有如此多的 CPU 密集型工作负载和后台任务,asyncio 调度延迟很容易主导尾部请求延迟。在针对初始服务发布进行调优之前,我们在 p99 及更高延迟的请求追踪中看到,尽管下游存储响应迅速,但请求经常因等待负责解析响应的协程被重新调度而停滞。

图 03 · 追踪 asyncio 延迟

并发不等于 CPU 并行

Python asyncio 允许并发处理请求,但在任何时刻,CPU 线程上仅执行单个请求。当有大量 CPU 工作需要完成时,这对请求延迟影响巨大。

重播
暂停
CPU 请求/响应处理
Python 网络读写
等待 Cosmos
低 CPU 工作
短暂的 Python 步骤;I/O 等待重叠

Python:请求 A · CPU 请求处理

时间 →
0
10
20
30
40
Python 线程
A
A
B
B
C
C
请求 A
CPU 请求
COSMOS
请求 B
等待第一个 Python 周期
COSMOS
请求 C
等待第一个 Python 周期
COSMOS

两种场景下,Cosmos + 网络等待:每个请求 6 个单位。

高 CPU 工作
漫长的 Python 步骤使就绪的响应处于等待状态

Python:请求 A · CPU 请求处理

时间 →
0
10
20
30
40
Python 线程
A
A
B
B
C
C
请求 A
CPU 请求
CPU 请求
COSMOS
就绪 · 阻塞
CPU 响应
请求 B
等待第一个 Python 周期
CPU 请求
COSMOS
就绪 · 阻塞
CPU 响应
请求 C
等待第一个 Python 周期
CPU 请求
COSMOS
就绪 · 阻塞
CPU 响应

两种场景下,Cosmos + 网络等待:每个请求 6 个单位。

说明性时间
0.0 / 40 说明性单位

对于 OpenAI 的 Python 服务,我们发现除了监测内存、CPU、网络和磁盘使用的标准利用率和饱和度指标外,还至关重要的是要监控 asyncio 循环及其繁忙程度,并据此进行调优。

通过定期调度后台任务并记录预期执行时间与实际执行时间之间的差异,我们能够实时经验性地测量事件循环的调度延迟。在高负载情况下,当存在大量耗时任务时,即使每个进程处理的并发请求数量适中,也足以产生显著的调度抖动,幅度可达数百毫秒,在某些极端情况下甚至长达数秒。

因此,我们采取的策略是让每个进程仅服务少量并发请求,并大规模扩展 Python 工作进程的数量。

降低功能标志配置中的尾部延迟

在我们的初始服务发布中,我们通过实时服务 CPU 剖析发现了一个导致高 asyncio 延迟(进而导致高尾部延迟)的根本原因:通过 Statsig(一个管理功能标志的工具,可用于运行 A/B 测试等)定期解析我们的功能标志配置的 JSON 数据。

默认情况下,Statsig 被配置为每分钟轮询刷新后的配置,且无抖动,而该配置包含了所有服务中的所有生产规则。在其他地方,架构决策决定在每个 Pod 中运行多达 8 个 Python 进程,以提高 CPU 利用率并提供更低的延迟。综合来看,这意味着每分钟每个 Pod 都会出现某个时刻,其所有工作进程都会停滞处理进行中的请求,转而将 CPU 周期用于解析一个巨大的配置文件。

一旦通过 CPU 剖析帮助我们找到问题根源,修复方案就很简单了:部署更小、更针对性的配置,延长刷新间隔,并为这类后台任务添加一些抖动。

平衡负载与管理连接池

为了保持较低的 asyncio 延迟,跨服务器进程良好地平衡请求负载也至关重要;如果不进行调优,连接池最终可能与这一目标背道而驰。

在使用客户端连接池的情况下,执行大量并发请求的单个客户端进程可能仅建立少量服务器连接,从而将其所有负载发送到少数几个进程中。在调整我们的负载均衡方式之前,我们的服务利用率存在巨大差异,某些尾部进程处理的并发请求数量是平均水平的 5 到 10 倍。

我们是在一次偶然事件中发现了这一点:尽管停止了过载我们部分服务的客户端,但一部分进程在突发流量过后仍然处于降级状态。事实上,我们注意到这些进程经历了失控的降级,接收的请求越来越多,直到我们重启它们才停止。一旦某个 Pod 过载,某些行为会将更多流量锁定到该过载的 Pod 上。这是我们一些队友从以往工作中非常熟悉的一类故障:亚稳态故障(opens in a new window)。

我们怀疑连接池是罪魁祸首,并通过限制最大连接重用持续时间来测试这一猜测,结果确实限制了降级情况,并证实了我们的调查方向。进一步调查发现,Python 的 aiohttp TCPConnector 默认采用 LIFO(后进先出)连接重用:最近返回的连接会被下一个请求选中。这通常是一个合理的默认设置:重用最近的连接允许为处理突发流量而创建的额外连接空闲超时,从而减少维护额外连接的开销。在这种情况下,它却为我们造成了亚稳态故障。在请求激增期间,对较慢且过载服务器的请求会更晚地将连接返回到池中,因此被后续请求更频繁地选中,逐渐将更多流量集中在已经挣扎的 Pod 上。修补连接池以使用 FIFO(先进先出)重用以打破这一反馈循环,甚至也降低了我们稳态下的请求方差。

图 04A · 客户端侧连接池

LIFO 将新工作发送回慢速进程

在经历了一波请求高峰后,较慢的服务器最后才将连接返回到连接池。后进先出(LIFO)机制促使更多工作集中在这些相同的慢速服务器上。

重播

暂停

初始的请求高峰同时到达 A、B 以及较慢的处理进程 C。

01
初始请求高峰
02
复用连接
03
结果

客户端进程

B3 = 到服务器进程 B 的连接 3。每个进程都有 t

原文链接:https://openai.com/index/scaling-storage-one-billion-users-part-one
来源:OpenAI
以上内容由 AI 自动翻译,仅供参考。
← 返回简报