当 ChatGPT 的全球用户突破八亿大关时,其背后的数据库基础设施正经历着前所未有的压力测试。OpenAI 在 PGConf.Dev 2025 的技术分享中揭示了一个令人惊讶的事实:支撑这款现象级 AI 产品的核心数据库并非分布式数据库,而是经过深度优化的 Azure 托管 PostgreSQL。这个案例为所有面临大规模读写压力的工程团队提供了宝贵的参考范本,本文将从读写分离策略、连接池优化与分区方案三个维度,拆解其中的工程化参数与实践要点。
一、架构选择:单主多副本的无分片路径
OpenAI 的 PostgreSQL 架构采用经典的单主多副本模式运行在 Azure Database for PostgreSQL Flexible Server 上,不依赖分片(Sharding)即可支撑数百万级别的查询每秒(QPS)。这一选择背后的核心逻辑是读多写少的业务特征 ——ChatGPT 的用户请求绝大多数是只读查询,只有极少数操作涉及数据写入。因此,架构设计的首要目标是最大化读扩展能力,同时将对主节点的写压力控制在可管理范围内。
主节点承载所有写入流量,而只读副本的数量可以根据负载动态扩展。OpenAI 在其核心业务中部署了数十个只读副本,其中部分副本分布在不同的地理区域以降低用户访问延迟。这种架构的优势在于其简洁性:无需维护复杂的分片键路由逻辑,也避免了跨分片事务的一致性难题。但其代价是主节点成为整个系统的写入瓶颈,所有写入操作最终都会汇聚到同一台主库上。
在实际部署中,OpenAI 选择使用 Azure 提供的最高规格服务器硬件来运行主节点。这并非过度配置,而是因为单主架构的性能上限直接取决于单机硬件的能力。根据行业经验,若主节点采用 64 核 CPU、512GB 内存并配备 NVMe SSD 存储,其理论写入能力可达到每秒数万次事务(TPS),足以支撑绝大多数互联网产品的峰值负载。关键在于后续章节将讨论的各种优化手段,确保这一硬件能力能够被充分释放。
二、读写分离策略:从路由到优先级隔离
2.1 请求分发与延迟控制
读写分离的第一步是正确识别只读请求并将其路由到合适的副本节点。在 OpenAI 的技术栈中,应用层通过 PgBouncer 连接池访问数据库,PgBouncer 可以根据连接字符串中的负载均衡参数将只读请求分发到不同副本。然而,单纯的轮询分发并不足以应对生产环境的复杂需求 —— 副本复制延迟可能造成数据一致性问题,某些强一致性要求的查询仍需回主节点执行。
OpenAI 的解决方案是为不同类型的请求分配优先级。高优先级请求(如付费用户的核心功能)被配置为访问专用的只读副本,这些副本通常与主节点保持极低的复制延迟;而低优先级请求(如后台数据分析)则可以使用普通副本,即使存在少量延迟也不影响业务正确性。这种软硬件协同的优先级隔离机制,确保了关键业务的稳定性不受批量任务的干扰。
从监控视角来看,复制延迟(Replication Lag)是读写分离架构中最需要关注的指标。OpenAI 在过去九个月内仅发生一次 SEV0 级别的 PostgreSQL 相关故障,其背后是对复制延迟的持续监控与告警。当延迟超过预设阈值(如 10 秒)时,系统会自动触发告警并限制写入速率,防止下游消费者读取到过于陈旧的数据。
2.2 主节点写压力削峰填谷
即便读流量被有效分流,主节点的写入压力仍是整个系统的性能瓶颈。OpenAI 采用了多层次的写压力控制策略来平滑写入峰值。首先,所有可转移的写入操作都被尽量卸载到异步队列中执行,而非同步阻塞用户请求。例如,用户行为日志、推荐系统特征等数据可以采用批量写入或延迟写入的方式,减少对主节点的瞬时压力。
其次,应用层实现了 Lazy Write 模式来平滑写入突发。当短时间内产生大量写入时,系统会先将数据暂存在内存或分布式缓存中,以可控的速率回写到数据库。这种削峰填谷的策略不仅降低了主节点的压力,也减少了因写入频繁导致的 MVCC 版本膨胀问题。
最后,数据回填(Backfill)操作的速率被严格控制。大规模数据迁移、批量更新等操作被限制在低峰时段执行,并且每次操作的行数设有上限。对于需要导入海量数据的场景,OpenAI 建议采用分批提交的方式,每批次控制在数千到数万行之间,确保事务日志(WAL)的生成速率不会对复制链路造成冲击。
三、连接池优化:PgBouncer 的多实例部署
PostgreSQL 的每个客户端连接都会消耗服务器端资源,包括内存用于维护连接状态、CPU 用于进程或线程调度。当并发连接数达到数千甚至数万级别时,连接管理本身就会成为性能瓶颈。PgBouncer 作为轻量级连接池中间件,通过连接复用大幅减少了后端 PostgreSQL 的连接压力。
3.1 多实例架构设计
单点 PgBouncer 无法满足大规模部署的需求 —— 它本身可能成为新的故障点,且其处理能力也存在上限。OpenAI 的方案是部署多个 PgBouncer 实例,由 Azure Load Balancer 在入口层进行流量分发。这种架构实现了两个目标:其一,消除单点故障,任何一个 PgBouncer 实例宕机不会导致服务整体不可用;其二,通过横向扩展提升整体吞吐量,支撑每秒数百万级别的查询请求。
在 Kubernetes 环境中,PgBouncer 通常以 Deployment 形式部署,并配置适当的副本数(建议 3 到 5 个)和资源限制(CPU 至少 500m,内存至少 512Mi)。Load Balancer 使用健康检查机制探测 PgBouncer 实例的存活状态,确保流量只会路由到健康的实例。
3.2 关键配置参数
PgBouncer 的性能调优涉及多个关键参数。首先是 max_client_conn,控制单个 PgBouncer 实例能够接受的最大客户端连接数,默认值通常偏低(100),对于高并发场景需要上调至数千级别。其次是 default_pool_size,指定每个用户 / 数据库组合在后端 PostgreSQL 中维护的连接数,建议设置为 20 到 50 之间,根据后端数据库的连接限额合理规划。
另一个重要参数是 pool_mode。对于短连接场景(如 HTTP 请求),事务模式(Transaction Mode)是常见选择,连接在事务结束后立即归还池中;对于需要使用预备事务(Prepared Statement)的场景,会话模式(Session Mode)更为合适。OpenAI 根据实际业务特征选择混合模式,对不同类型的查询使用不同的池模式,以平衡连接复用率与功能兼容性。
连接超时参数同样需要关注。client_idle_timeout 控制客户端连接在空闲多长时间后被断开,默认值为 30 分钟,可以根据业务特点适当缩短以释放无效连接;server_idle_timeout 控制 PgBouncer 与后端数据库连接的空闲超时,应设置为略长于客户端超时以确保连接可用性。
四、分区方案:范围分区与清理策略
4.1 分区表设计原则
随着数据量的持续增长,单表查询性能会逐渐下降,索引维护成本也会显著上升。分区表(Partitioning)通过将大表拆分为多个物理子表,实现了查询剪裁(Partition Pruning)与并行扫描,是管理海量数据的有效手段。OpenAI 在其 PostgreSQL 集群中采用了范围分区(Range Partitioning),按时间维度对日志类、流水类数据进行切分。
典型的分区设计以月为单位创建子表,每月一个分区。分区键的选择至关重要 —— 应选择查询中最常用于过滤条件的列,如 created_at、event_date 等时间类型字段。当查询条件包含分区键时,查询优化器可以自动排除不相关的子表,将扫描范围限定在少数分区上,从而大幅提升查询效率。
创建分区表时需要特别注意锁竞争问题。PostgreSQL 11 及以上版本支持 CONCURRENTLY 选项的分区创建操作,允许在不阻塞并发读写的情况下添加新分区。对于生产环境中的在线分区切换,建议始终使用 CONCURRENTLY 参数,并在业务低峰期执行。
4.2 分区维护与清理
分区表的有效期管理是运维工作的重点。OpenAI 实施了严格的分区保留策略 —— 超过保留期限的分区会被及时删除,以控制存储成本与查询性能。具体保留时长取决于业务需求与合规要求,例如用户行为日志通常保留 30 到 90 天,而核心业务数据可能需要保留数年。
删除过期分区的操作本身也需要谨慎处理。直接使用 DROP TABLE 会持有排他锁,可能阻塞并发查询。正确的做法是先将该分区 Detach(分离)为独立表,确认业务无影响后再执行删除。对于海量数据场景,建议先执行 TRUNCATE 释放空间,再择期执行 DROP 以降低锁持有时间。
分区表还与 Autovacuum 优化紧密相关。每个分区都是独立的物理表,需要独立进行垃圾回收与索引维护。历史分区因不再写入新数据,其膨胀问题相对可控;而活跃分区的 Vacuum 频率应适当提高,避免因 MVCC 版本积累导致的查询性能劣化。
五、监控与参数调优
5.1 核心监控指标
大规模 PostgreSQL 运维离不开完善的监控体系。OpenAI 使用 Datadog 作为监控平台,重点关注以下指标:QPS 与 TPS 分别反映读写负载水平;复制延迟反映副本同步状态;连接数使用率反映连接池健康度;MVCC 垃圾行比例反映 Autovacuum 是否及时;缓存命中率反映内存配置是否合理。
慢查询监控同样不可或缺。OpenAI 发现长事务会阻塞垃圾回收并消耗资源,因此配置了 idle_in_transaction_timeout 参数(建议设置为 5 到 10 秒),自动终止空闲超过阈值的会话。此外,statement_timeout 参数用于限制单条查询的执行时间,避免异常查询长期占用资源。
5.2 关键参数配置
PostgreSQL 的默认参数往往偏保守,需要根据实际硬件配置进行调整。shared_buffers 是最重要的内存参数,建议设置为物理内存的 25%(如 128GB 物理内存可设置为 32GB),但不超过 8GB 的倍数以优化大页分配。effective_cache_size 反映操作系统缓存的预估大小,查询优化器据此判断是否使用索引,建议设置为物理内存的 50% 到 75%。
work_mem 控制排序、哈希连接等操作的内存分配,单个查询可能使用多个 work_mem,因此设置过高可能导致内存不足。建议从 4MB 到 64MB 开始,根据慢查询日志中排序类操作的实际情况逐步调整。maintenance_work_mem 用于 VACUUM、CREATE INDEX 等维护操作,通常设置为 work_mem 的 10 倍左右。
WAL 相关参数也需要关注。wal_buffers 设置 WAL 缓冲区的内存大小,对于高写入负载可适当上调至 64MB 到 256MB;checkpoint_completion_target 控制检查点的平滑程度,建议设置为 0.9 以分散检查点压力;max_wal_size 与 min_wal_size 控制 WAL 文件的自动清理策略,避免磁盘空间不足。
六、故障案例与经验教训
OpenAI 在分享中披露了两个典型故障案例。第一个是级联故障 ——Redis 服务异常导致大量请求涌入 PostgreSQL 主节点,最终引发数据库雪崩。这个教训强调了外部依赖故障的隔离设计,每个下游服务都应实现熔断机制,防止局部故障扩散到整个系统。
第二个案例更为隐蔽:高 CPU 使用率触发了 PostgreSQL WALSender 进程的一个 Bug,CPU 恢复后 WALSender 进入自旋循环,持续占用 CPU 并拒绝发送 WAL 数据,导致复制延迟急剧增加。这个案例提醒我们,除了常规监控外,还需要对数据库内核的已知问题保持关注,并在异常时刻及时介入。
结语
OpenAI 的实践表明,即便面对八亿用户的规模,PostgreSQL 凭借成熟的生态与丰富的优化手段,依然能够胜任核心数据存储的角色。关键在于理解业务的读写特征,合理规划读写分离架构,并通过连接池、参数调优、监控告警等手段持续打磨系统稳定性。这套方法论对于任何面临大规模数据访问挑战的工程团队,都具有直接的借鉴价值。
资料来源:本文基于 OpenAI 基础设施工程师 Bohan Zhang 在 PGConf.Dev 2025 的演讲《Scaling Postgres to the next level at OpenAI》整理,演讲视频与幻灯片可见 PGConf.Dev 2025 官方议程页面。