Skip to content

【tech blog review】 How ChatGPT Uses PostgreSQL

· 11 min
TL;DR

OpenAI 如何利用单主多副本架构的 PostgreSQL 支持了 8 亿 ChatGPT 用户

正文#

最近在 OpenAI 官网看了一篇博客 Scaling PostgreSQL to power 800 million ChatGPT users ,讲述了他们如何使用并优化 PostgreSQL 来支持 ChatGPT 的海量用户。我觉得有意思的是他们遇到的问题也是很多开发者都会遇到的问题。而唯一不同的是,他们面对的是 8 亿用户(并且还在快速增长),而我们面对的可能只是几千、几万用户。同样的问题,在不同规模下,解决方法可能会有所不同,所以这篇博客值得学习。

首先,OpenAI 的 PostgreSQL 集群架构是单主多副本架构,主库负责写操作(也有一些无法分离的读请求),副本库负责读操作。

即使这种架构满足日常的请求规模,但是也会遇到一些问题,比如:新功能上线导致流量激增,主库面临写风暴,比如大量缓存失效,比如执行大量复杂的多表连接查询,导致消耗大量的 CPU 资源,导致数据库 CPU 饱和,影响正常请求的处理等等。 另外 PostgreSQL 的多版本并发控制(MVCC)在写入密集场景下会导致写入放大、表膨胀和复杂的清理(Vacuum)问题,这些问题在高并发场景下会更加明显,从而导致主库的性能下降,影响整体的用户体验。

但是即使是这样,OpenAI 首先仍然没有选择数据库分片,因为他们认为这样做太复杂了,可能需要修改应用程序端点,增加了架构的复杂度,也非常耗时,对应短期的目标来说,这样不划算,也不是最高优先级事项。

更重要的是,在当前架构下,他们做了很多优化已经想到好了,能够支撑现有的流量。

优化#

1. 减轻主库负载#

2. 优化异常查询#

持续优化 PostgreSQL 查询,避免多表连接等 OLTP 反模式,并将复杂连接逻辑转移到应用程序层。 很多这类查询来源于 ORM, 所示使用 ORM 时需要注意查询的性能,避免使用复杂的查询和连接,尽量使用原生 SQL

配置连接超时,防止空闲连接长时间占用资源。

3. 单节点故障转移#

虽然单个副本节点出现故障可以马上切换到其他副节点,但是如果主节点挂了呢?因为是单主节点,所以此时没有替代节点转移故障。

解决办法是:首先主节点设置为高可用模式(HA)。将主数据库运行在带有热备的高可用性模式下,并部署多个副本以应对读取副本故障。

大多数关键请求仅涉及读取查询。为了减少主节点的单点故障,将这些读取从主节点转移到副本,确保即使主节点宕机,这些请求也能继续服务。虽然写入作仍会失败,但影响会减少;由于读取仍然可用,它不再是 SEV0。

为了应对读副本失败,在每个区域部署多个副本,并保持足够的容量余量,确保单个副本故障不会导致区域性中断。

4. 工作负载隔离#

有些请求会消耗数据节点过多的资源,而有些则很少。这意味着可能某些数据库节点中高消耗的请求会影响其余的请求。

解决方法就是将请求分为高优先级和低优先级,并路由到不同的实例,以防止资源密集型请求影响其他工作负载

5. 连接管理#

每个 PostgreSQL 实例的连接数限制为 5,000 (Azure PostgreSQL),如果不加控制很容易消耗完并造成严重的事故。

在 PostgreSQL 中,可以使用 PgBouncer 作为代理层进行连接池管理,以提高连接的复用率和性能。

另外跨区域连接和请求可能成本高昂,因此将代理、客户端和副本共置在同一区域,以最大限度减少网络开销和连接使用时间。

6. 缓存#

最怕的是缓存失效,因为缓存失效会导致大量的读请求,从而增加主库的负载。

对于 OpenAI 来说,数据库的流量是读密集型的。对于大部分读流量,他们又专门增加了一层缓存,但是如果缓存命中率突然意外下降,则大量请求直接打到数据库实例上,也是吃不消的。 为此他们又专门加了一种缓存锁机制:当多个请求未命中同一个缓存 key 时,只有一个请求获得锁并继续检索数据并重新补充缓存。其他所有请求都是等待这个缓存 key 更新,而不是一次性全部发送到 PostgreSQL 实例。这种方式也是值得借鉴的。

7. 扩展读复制#

对于 OpenAI 的这种架构,主节点需要同时向近 50 个副本发送 WAL 数据,这会消耗大量的网络带宽和 CPU 资源。 比如,如果主节点每秒产生 100MB 的日志,发送给 50 个副本就意味着每秒要消耗 5GB 的网络带宽。另外维护这 50 个网络连接并分发数据会消耗主节点大量的 CPU 资源。如果主节点太忙,日志发不过去,从节点的数据就会落后于主节点,导致用户读到旧数据。

为了减少主节点的负载,他们与 Azure PostgreSQL 团队合作,研究级联复制,以支持更多副本并减轻主数据库的 WAL 流式传输压力。级联复制通过将主节点的 WAL 数据流式传输到多个副本节点,从而减少主节点的负载。

PRIMARY WAL WAL ... INTERMEDIATE REPLICA INTERMEDIATE REPLICA WAL WAL ... WAL WAL ... READ REPLICA READ REPLICA READ REPLICA READ REPLICA

8. 限流#

在应用程序、连接池、代理和查询层实施限速,以防止流量高峰导致数据库过载。这属于常规方法了。

9. Schema 管理#

对于 ChatGPT 这种拥有 8 亿用户、数据量极其庞大的系统中,需要特别避免全表重写。 但是即使是小的 Schema 更改,比如更改列类型,也可能触发完整的表重写。因此,需要谨慎地应用 Schema 更改,限制它们只做轻量级操作,避免重写整个表。

例如: