当存储平台同时服务大量产品,可靠性问题常常来自资源边界与组织边界没有对齐。某个小功能的热点键可能挤占登录服务所在物理分区;把功能拆进独立容器后,又可能在发布当天因没人预扩容而失败。OpenAI 的 Habitat 第二篇工程报告最值得读的,是这次从共享到独占、再回到默认共享的迭代,以及缓存为什么必须和变更传播一起设计。原文:Rapidly scaling online storage to serve over 1 billion ChatGPT users (Part II)。
Jon Lee、Hrachya Hakobyan、Chaomin Yu · OpenAI · 2026-10-23
逻辑分区不是独立的性能预算
Habitat 最初主要依赖 Azure Cosmos DB 的原生能力,把多个产品放在共享容器中。客户端可以选择逻辑分区键,相同的 user_id 或 workspace_id 因而可能被多个用途复用。更深一层的问题是,吞吐限制在物理分区执行,多个逻辑键共享同一预算:文档里的单键上限不等于每个键都能独占那份吞吐。
数据增长也会改变性能边界。容器总预配吞吐不变时,存储容量触发物理分区分裂,分到每个分区的吞吐就会减少。请求率即使没有增长,活跃键也可能落入更弱的分区。这种故障需要同时看存储体积、分区数量和吞吐分配,单看 QPS 曲线不一定能找到原因。
独占换来了隔离,也分散了容量责任
团队随后为重要用途建立独立容器,降低跨产品故障扩散,并用脚本随物理分区数调整预配吞吐。问题是,每个产品团队都需要知道自己的发布节奏和未来需求。共享池里很小的一次增长,放到新独占容器中可能是千倍跳升。
因此,单租户减少了邻居干扰,却失去了负载混合带来的余量。文章并没有据此否定独占,而是说明隔离策略同时也是容量规划策略。若平台无法吸收规划职责,简单增加容器会让更多团队承担自己并不擅长的数据库运维工作。
回到共享需要更精细的控制
第三阶段的默认共享建立在三类改进上。热点键优先被节流,避免吞掉整个物理分区;批量插入、抽取等离线请求使用较低 QoS,先于交互请求受到限制;改进后的 serverless 模式支持按 RU 使用量和存储量分裂,单物理分区可扩至 10K RU/s。
这些能力共同改变了共享池的失效方式,不能把成效只归因于“用了 serverless”。文章报告改动后数月没有再出现容量配置或 noisy-neighbor 事故,这是团队的运行观察,不是跨所有租户、所有负载的严格无故障保证。独占与数据移动能力仍可用于需要特殊隔离的场景。
托管服务仍需验证实际规模的恢复
一次区域断电暴露出大型账户切换可能需要数小时,之后回切又因连接蜂拥冲击网关。问题不是供应商完全没有容灾功能,而是双方都未在这个规模验证其行为。团队随后建立接近生产规模的非生产账户,共同测试 SDK、网关容量、连接池与元数据缓存。
可迁移的可靠性启发是:故障转移和回切应分别测试。正常读写吞吐、账户控制面操作和连接恢复的瓶颈并不相同。产品侧只看到“数据库不可用”,但缓解措施可能要跨客户端、代理、云服务控制面共同完成。
全球读性能由三层改进叠加
超过 90% 的 Habitat 请求是读。把数据放近用户能降低延迟,但云区域的存储、电力和散热容量会限制布局;全面复制又会放大容量需求。因此只有较小且最敏感的数据集广泛复制,其余依靠靠近计算侧的 Valkey lookaside 缓存,再减少代理自身开销。
缓存新鲜度不能只靠很短的 TTL,因为那会让命中率下降;很长的 TTL 又会增加陈旧读取。Habitat 消费 Cosmos DB change feed,把更新转成全球缓存失效通知,目标是在数秒内传播。下一次读取再回源填充。这里的关键是变更传播速度与可靠性,并不意味着缓存已经提供全局强一致读取。
12 倍改善属于全套改造
作者报告全球读 p50 从约 120 ms 降到约 10 ms。贡献包括关键数据就近复制、区域缓存和 Rust 重写代理,不能把 12 倍全部归到 Rust,也不能把中位数改善当成长尾同幅下降。未命中请求仍可能跨远距离区域读取,数据位置问题依旧存在。
CDC 同时支撑索引、迁移和分析,规模达到每秒数百万变更。其运行需要租约与 worker 生命周期管理、流间故障隔离、死信队列和跨区读取切换;永久投递失败不能堵住整个队列。这些细节说明,加缓存之后,变更通道本身成为可靠性关键路径。原文配图为交互组件,未取得适合保留为独立静态原图的版本,本篇用机制说明保留这些依赖关系。
用同一请求串起容量、缓存与恢复
从运维角度,一个用户读请求可能先经过身份检查和数据位置路由,再命中区域缓存,或回源到远端物理分区。每一层都能改变延迟,也能改变故障扩散范围。因此总体延迟下降之后,仍需要把命中与未命中、交互与离线流量、正常运行与恢复阶段分开观察;不然中位数会掩盖很少发生、却影响关键登录路径的异常。
缓存失效通道又与基础数据库不同步运行。变更积压可能先表现为陈旧结果,再表现为重试、用户刷新和额外读流量。文章并未给出这些后续效应的统一量化模型,但从依赖关系可以推导,变更队列积压和消费延迟应与请求指标放在一起看。只监控数据库错误率无法完整解释缓存层行为。
类似地,隔离不是把所有请求平均分流。交互请求与批量抽取价值不同,适合在资源紧张时有不同优先级;容量和地理布局也不是独立决策,给近端存储更多空间可能减少可用于推理的容量。本文最有启发的部分,正是这些局部优化必须服从产品层的延迟与可用性目标,而非每一层各自追求最高利用率。
另外,原文提出的新存储系统仍在开发中,以降低闪存容量需求交换较低峰值吞吐。应将这部分看作未来方向,与当前 Cosmos DB 和缓存改造已观察到的结果分开。