OpenAI 发布工程博客《Rapidly scaling online storage to serve over 1 billion ChatGPT users》第一篇,公开其自研在线存储平台 Habitat 的完整演进过程。Habitat 目前每秒处理超过 7000 万次请求,支撑每周超过 10 亿人使用的产品,覆盖近 40 个地理区域,服务数据量超过 500 PB。两年前它还只是一个连接单个数据库的 Python 客户端库,今天已经是一套复杂的分布式系统。
这篇文章是「如何扩展在线存储」两篇系列的第一篇,重点讲述 Habitat 的演化路径:为什么把它从客户端库改造成独立服务,以及如何用一个非常规的服务端语言——Python——把它撑成可靠的存储平台层。第二篇将讨论大规模多租户可靠性、分层读取性能优化,以及与 Azure Cosmos DB 的合作扩展。
Habitat 是什么
每个 OpenAI 产品都依赖快速、可靠的数据访问:无论是用户登录、查看 Codex 设置,还是在 ChatGPT 里开一段新对话。这些动作在得到响应前可能需要多次独立的数据查询。请求慢,产品就慢;请求失败,产品就完全不可用。
Habitat 的出发点很简单:产品工程师不应该需要思考数据库管理。它在 2024 年年中作为一个小型 Python 库诞生,与 ChatGPT 主服务器交互,底层把操作映射到数据库应用 Azure Cosmos DB。这个库负责判断数据属于什么类型、应该从哪里读写、请求是否被允许等琐碎工作;产品工程师不需要关心 schema 查询、路由、鉴权、加密、序列化、请求整形和连接池,也不需要知道数据来自 Cosmos DB、缓存还是其他存储。

这个 Python 库运行良好,尽管 OpenAI 并没有集中推动迁移,Habitat 在产品工程师中仍获得了快速采用。随着产品需求变化,开发者也很容易在共享库中追加客户端缓存、压缩或加密等能力。
把库改造成服务
到 2025 年中,Habitat 作为客户端实现已经触到上限。随着 Habitat 层变复杂、OpenAI 服务数量增加,保持向后兼容的协议变更变得不可行。
一个典型例子是:为了降低单个区域故障对关键数据集的影响,团队希望把数据迁移到一组按区域分布的 Azure Cosmos DB 账户上。这个改动需要在客户端引入额外的路由逻辑,默认关闭在特性开关(feature flag)之后,先确保它铺到所有客户端,再打开开关。跨数十个服务协调部署、逐个团队推进,花了好几天。在启用之前,团队意识到应该先做一部分影子流量验证分片逻辑是否正确,又是好几天;再修一个后来发现的 bug,又是好几天。等到终于可以打开开关时,却有团队因为无关原因回滚到此前有 bug 的客户端版本,最终引发了整个团队努力想避免的那次故障。
客户端库的改动需要跨数十个服务做复杂协调,这个过程越来越脆弱、低效且容易引发运维失败。为了减少这种运维面的扩散,OpenAI 决定把 Habitat 抽成独立服务。
把存储逻辑解耦到独立服务后,部署、可观测性和平台能力增强都有了单一控制点。与其管理碎片化的更新,团队可以集中落地改进,让每个 OpenAI 产品立即受益。集中式服务同时提供了唯一的数据安全与隐私控制关口:在这里统一执行访问控制策略、做审计日志,并限制对 Cosmos DB 等底层存储资源的访问。Habitat 在保护用户数据、阻止外部、内部与智能体行为体的未授权访问上扮演关键角色。

用 Python 把服务撑到大规模
团队确定需要独立服务,但还不想马上放弃 Python,即便 Python 作为服务会带来额外开销。用 Python 承载高吞吐服务会增加网络延迟,并相比本地库执行显著抬高 CPU 与内存的扩展成本。团队也清楚,Python 的低效在 100 倍规模下将不可接受,最终重写几乎不可避免。
但他们把这视为一次战略性的技术债:当时的首要目标不是成本或资源优化,而是解锁产品开发者、拿到平台稳定性。短期接受 Python 服务的性能折衷,就能优先处理更紧迫的挑战、确立核心 API 并搭建健壮的基础设施。团队还做了一个经过计算的押注——自家编程模型的快速进步会简化未来的技术路径;等到必须彻底迁移时,Codex 和 GPT 能让迁移变得可行。这个押注后来被证明是对的。
不过,把 Habitat 跑成 Python 服务意味着不接受更差的延迟。当用户的一次平均请求会引发数百次数据库调用时,用户能感知到的是最慢的那次数据库调用。在如此规模上运行 Python 服务,主要挑战就在于管理这些尾延迟。
跟踪 asyncio 调度延迟
asyncio 能帮助 Python 并发执行 I/O 密集型工作负载,但无法绕开 GIL 提供 CPU 并行。除了 I/O 密集的请求代理,Habitat 还要处理大量 CPU 密集型职责与后台任务:路由、压缩、加密、校验和、下游健康检查、请求影子与对冲请求。
CPU 密集型任务与后台任务如此之多,asyncio 的调度延迟很容易主导尾延迟。在初次上线前的调优阶段,团队在 p99 及以上延迟的请求链路里看到:下游存储响应很快,但请求经常卡在等待对应协程被重新调度、以解析响应上。

对 OpenAI 的 Python 服务来说,除了内存、CPU、网络和磁盘的标准利用率与饱和度指标,监控 asyncio 事件循环的繁忙程度并据此调优同样关键。团队定期调度后台任务,记录预期执行时间与实际执行时间的差值,从而实时测量事件循环调度延迟。在高利用率、大量昂贵任务的情况下,即便每个进程只处理少量并发请求,也足以产生明显的调度抖动——可达数百毫秒,个别边缘情况甚至达到数秒。因此他们选择让每个进程只服务少量并发请求,转而大规模横向扩展 Python 工作进程。
一个特性开关配置引发的尾延迟
服务初次上线时,团队通过线上 CPU profiling 找到了 asyncio 延迟偏高的一个根因:Statsig(用于管理特性开关、A/B 测试等)配置的周期性 JSON 解析。
默认配置下,Statsig 每分钟拉取一次刷新后的配置且没有抖动,而这份配置包含所有服务的全部生产规则。另一个架构决策是每个 Pod 最多运行 8 个 Python 进程以推高 CPU 利用率、降低延迟。两者叠加的结果是:每分钟每个 Pod 都会有一个瞬间,所有 worker 停止处理在途请求,把 CPU 周期花在解析一个巨大配置文件上。定位到问题后修复方式很直接:下发更小的针对性配置、拉长刷新间隔,并为这类后台任务加入抖动。
连接池与 metastable failure
要维持较低的 asyncio 延迟,还必须让请求在服务进程之间均衡分布——如果不加调优,连接池反而可能与之冲突。客户端连接池会让一个发出大量并发请求的客户端进程只建立少量服务端连接,从而把全部负载压到少数进程上。在调整负载均衡方式之前,服务利用率的方差很大,部分尾部进程承载的并发请求数是平均值的 5 到 10 倍。
这问题是在一次偶然事故中被发现的:尽管造成过载的客户端已经停止,一部分进程在突发流量结束后很久仍然处于降级状态,甚至出现失控式恶化,收到的请求越来越多,直到进程被重启。一旦某个 Pod 过载,某些行为反而会把更多流量固定到这台过载的 Pod 上,这是一类被称为 metastable failure(亚稳态故障)的失效模式。
团队怀疑是连接池的问题,并通过限制连接的最大复用时长验证了这个猜想——限制连接复用确实抑制了恶化,也确认了排查方向。进一步追查发现,Python 的 aiohttp TCPConnector 默认采用 LIFO(后进先出)连接复用:最近归还的连接会被用于下一个请求。这个默认值通常合理,能减少维持额外连接的开销;但在这里它制造了亚稳态故障。突发请求期间,发往较慢过载服务器的连接归还得更晚,因此被后续请求更频繁地选中,流量逐渐向本就吃力的 Pod 集中。把连接池改为 FIFO 复用打断了这个反馈回路,还顺带降低了稳态下的请求方差。


如今 OpenAI 主要依靠 Istio 与 Envoy 在全公司基础设施中提供连接池与更贴近服务端负载的均衡策略,从根本上避开这个问题。
避免压垮下游资源
为降低 asyncio 延迟、同时运行大量 Python 进程有一个副作用:庞大的连接数很容易压垮下游依赖,也就是所谓的「惊群」(thundering herd)。一次日常部署如果没有为慢节奏做调优,连接反复创建销毁就会造成明显的 CPU 抖动;一次连接泄漏则可能占满 NAT 网关、直接打瘫网络。这些问题在其他服务上并不罕见,但进程数多出一个数量级,会显著降低触发阈值,往往在单纯吞吐量之外打满客户端根本没预期需要在稳态下处理的网络相关资源。
团队同样依赖 Envoy 把连接汇聚起来:它把 Python 的 HTTP/1 连接升级为 HTTP/2 以利用多路复用,再对这些连接做池化并延长生命周期。Envoy 还提供了集中实施限流与熔断的位置——这两件事放在每个独立的 Python 进程里效果会差很多。

为什么 Habitat 刻意「做得少」
Habitat 能把 Python 撑到这一步,原因之一是它受限的 API——这让请求成本变得可预测。它不允客户端构造随意的 SQL 查询(那可能导致大范围扫表或跨多表 join),而是暴露一套简单的 NoSQL API。缺少强大 API 是 Habitat 设计中的一项显式取舍。
团队的目标是优化简单、可预测、工作量恒定的请求。这类系统在扩展上要容易得多,也很难被用错。扇出不可预测的请求在运维上是危险的:它们让隔离与负载均衡变得复杂,还会引入服务及其客户端都难以扩展的延迟悬崖。
在迁移到 Habitat 与 Azure Cosmos DB 之前,OpenAI 的在线数据大多存在 Postgres 上。那时审查所有查询与 schema 变更、确认它们在索引数据上表现良好再上线是容易的;随着团队与产品增长,这件事很快变得难以管理,并且成为故障的常见原因——热路径上一个昂贵的查询就能拖垮整个数据库。问题本质是成本不对等:写出一条昂贵且难以运行的 SQL 查询很容易。Habitat 让昂贵的查询在客户端就变得极其显眼,不存在能压垮 Habitat 的无界查询;复杂 join 与图遍历需要产品团队自己承担一部分重活,从而在整体上推动更高效的设计。
Habitat 暴露的 NoSQL API 围绕客户端自定义的对象与边类型构建,灵感来自 TAO(Facebook 的图存储系统)。客户端预先定义对象、边及其相互关系,但不定义每个类型的内容。由此形成的关系形似图,但 Habitat 本身并不支持典型的图遍历查询,只能查询某个对象的直接边。
团队把这张图做了分区,使每个对象及其对应的边共置于同一个存储层分区中,但并不会在数据库层面刻意让对象与其边指向的远端对象共置。结果是这个模型易于分区、便于水平扩展,但图遍历效率不高:对象之间的任意一次跳转,都可能需要从位于不同区域的两个完全不同的 Cosmos DB 账户读取数据。
对查询需求更复杂的客户端,OpenAI 通过 Rockset 提供一份离线的 Habitat 二级视图:用变更数据捕获(CDC)把在线存储的变更近实时地写入隔离的 Rockset 实例,每个客户端团队负责为自己的复杂查询扩展各自的 Rockset 实例。这给客户端带来了额外摩擦,但团队认为在这个时间点这是正确的取舍——让简单查询成为默认,同时为需要复杂查询的人留一扇后门,从而把读写密集的分析与搜索负载与在线存储隔离开来。
从 Python 迁移到 Rust
把 Python 重写推迟一年,让团队在高速增长期能专注于更紧迫、更有影响力的问题。随着平台成熟、增长持续加速,Habitat 已成为 OpenAI 核心数第二大的服务(Envoy 规模第四),迁移的时机终于到了。Python 版本的峰值曾支撑每秒超过 2000 万次请求。
2026 年第二季度,仅用 2 名工程师、Codex 和 GPT-5.5,团队就把整个服务用 Rust 重写完成。新的 Rust 服务目前承载 95% 的生产请求,Python 版本将在未来几周内彻底下线。数据显示,Rust 服务比 Python 版本CPU 效率高 6 倍、内存效率高 15 倍,平均延迟和尾延迟都显著更低。团队计划在后续博客中分享更多经验。
核心总结
- Habitat 是 OpenAI 自研的在线存储平台,每秒处理超过 7000 万次请求,支撑 10 亿周活用户、近 40 个地理区域与 500 PB 数据
- 它 2024 年年中从一个小型 Python 客户端库起步,2025 年中因跨服务协调成本过高被改造为独立服务,以获得部署、可观测性、安全与隐私的单一控制点
- 用 Python 承载高吞吐服务的主要挑战是 asyncio 调度延迟与尾延迟:Statsig 配置的周期性 JSON 解析、LIFO 连接复用导致的 metastable failure 都是被逐一排查修复的具体根因
- Habitat 刻意保持受限的 NoSQL API,避免无界查询与成本不对等;复杂查询通过 CDC 同步到 Rockset 离线视图
- 2026 年第二季度,2 名工程师借助 Codex 与 GPT-5.5 用 Rust 完成重写,目前承载 95% 生产请求,CPU 效率提升 6 倍、内存效率提升 15 倍,Python 将在数周内下线
原文:Rapidly scaling online storage to serve over 1 billion ChatGPT users(OpenAI,2026 年 9 月)


