Scaling PostgreSQL to power 800 million ChatGPT users
Blog

Scaling PostgreSQL to power 800 million ChatGPT users

2026.01.24
·Service·by 이호민
#PostgreSQL#Scalability#Database#OpenAI#High Availability

핵심 포인트

  • 1OpenAI는 ChatGPT 사용자 기반의 폭발적인 증가로 PostgreSQL 부하가 10배 이상 증가하자, MVCC로 인한 쓰기 부하 문제를 해결하며 읽기 중심의 워크로드를 위한 대규모 스케일링을 진행했습니다.
  • 2이를 위해 쓰기 집약적인 워크로드를 Azure Cosmos DB와 같은 sharded 시스템으로 마이그레이션하고, 쿼리 최적화, PgBouncer를 통한 connection pooling, 캐싱 및 워크로드 격리 등 광범위한 최적화를 구현했습니다.
  • 3결과적으로 단일 primary 인스턴스와 약 50개의 read replica로 초당 수백만 QPS를 처리하며 8억 명의 ChatGPT 사용자를 지원하고 있으며, 향후 cascading replication을 통한 추가 read replica 확장 및 쓰기 오프로드를 계속 추진할 계획입니다.

OpenAI는 ChatGPT 사용자가 8억 명에 달하고 PostgreSQL 부하가 1년 만에 10배 이상 증가하는 등 빠르게 성장하는 사용자 기반에 맞춰 PostgreSQL을 확장해야 하는 과제에 직면했습니다. 이 논문은 PostgreSQL이 읽기 중심(read-heavy) 워크로드에 대해 이전에 생각했던 것보다 훨씬 더 큰 규모로 안정적으로 확장될 수 있다는 새로운 통찰력을 바탕으로, 단일 primary Azure PostgreSQL flexible server 인스턴스와 전 세계에 분산된 약 50개의 read replica를 통해 수백만 QPS(Queries Per Second)를 지원한 경험을 상세히 설명합니다.

초기 설계는 트래픽 증가에 따라 PostgreSQL 및 애플리케이션 계층에서의 최적화와 instance scale-up 및 read replica 추가를 통해 대응했습니다. 그러나 단일 primary 아키텍처는 예상치 못한 과부하 상황(예: cache layer 장애로 인한 광범위한 cache miss, 값비싼 multi-way join의 surge, 신규 기능 출시로 인한 write storm)에서 SEV(Severity)를 발생시켰습니다. 특히 PostgreSQL의 MVCC(Multiversion Concurrency Control) 구현은 write-heavy 워크로드에서 비효율적입니다. MVCC는 tuple 업데이트 시 전체 row를 복사하여 새로운 버전을 생성하므로 write amplification이 발생하며, 읽기 시에도 dead tuple을 스캔해야 하므로 read amplification이 발생합니다. 이는 또한 table 및 index bloat, index maintenance 오버헤드, 복잡한 autovacuum 튜닝과 같은 추가적인 문제를 야기합니다.

이러한 한계를 완화하고 write pressure를 줄이기 위해, OpenAI는 shardable하고 write-heavy한 워크로드(예: horizontal partitioning이 가능한 워크로드)를 Azure Cosmos DB와 같은 sharded system으로 마이그레이션했습니다. 동시에 애플리케이션 로직을 최적화하여 불필요한 write를 최소화했습니다. 기존 PostgreSQL 배포에는 더 이상 새로운 table을 추가하지 않으며, 신규 워크로드는 기본적으로 sharded system을 사용합니다. 기존 워크로드의 sharding은 복잡하여 현재는 단일 primary 인스턴스가 모든 write를 처리하는 unsharded 아키텍처를 유지하고 있습니다.

PostgreSQL을 수백만 QPS 규모로 확장하기 위해 다음과 같은 광범위한 최적화와 해결책이 구현되었습니다:

  1. Reducing load on the primary:
    • 문제: 단일 writer는 write를 확장할 수 없으며, heavy write spike는 primary를 빠르게 과부하시킵니다.
    • 해결책: primary의 부하(읽기 및 쓰기 모두)를 최소화합니다. 가능한 모든 읽기 트래픽은 replica로 offload하며, write transaction의 일부인 읽기 쿼리는 효율성을 보장합니다. Shardable한 write-heavy 워크로드는 Azure Cosmos DB로 마이그레이션되었고, sharding이 어려운 write-heavy 워크로드도 마이그레이션이 진행 중입니다. 애플리케이션을 최적화하여 중복 write를 수정하고, traffic spike를 완화하기 위해 lazy writes를 도입했습니다. Table field backfill 시 strict rate limit를 적용하여 과도한 write pressure를 방지합니다.
  1. Query optimization:
    • 문제: 많은 table을 join하는 값비싼 쿼리들이 CPU를 많이 소모하여 서비스 성능을 저하시킵니다. ORM(Object-Relational Mapping) 프레임워크에 의해 생성되는 쿼리가 문제가 될 수 있습니다.
    • 해결책: 지속적으로 PostgreSQL 쿼리를 최적화하여 효율성을 높이고 OLTP(Online Transaction Processing) anti-patterns를 피합니다. 복잡한 multi-table join은 피하고, 필요한 경우 join 로직을 애플리케이션 계층으로 이동합니다. ORM이 생성하는 SQL을 신중하게 검토합니다. idle_in_transaction_session_timeout과 같은 timeout 설정을 통해 장시간 실행되는 idle 쿼리가 autovacuum을 차단하는 것을 방지합니다.
  1. Single point of failure mitigation:
    • 문제: primary는 단일 장애점이며, primary 실패 시 전체 서비스에 영향을 미칩니다.
    • 해결책: 대부분의 중요 요청은 읽기 쿼리이므로, 이들을 primary에서 replica로 offload하여 primary 실패 시에도 읽기 서비스가 지속되도록 합니다. Primary는 HA(High-Availability) 모드로 hot standby와 함께 운영되어 지속적으로 동기화되며, 장애 시 신속하게 standby를 승격시켜 downtime을 최소화합니다. Read replica의 장애를 처리하기 위해 각 지역에 충분한 용량 headroom을 가진 여러 replica를 배포합니다.
  1. Workload isolation:
    • 문제: 특정 요청이 PostgreSQL 인스턴스에서 불균형하게 많은 자원을 소모하여 다른 워크로드의 성능을 저하시킬 수 있습니다.
    • 해결책: 'noisy neighbor' 문제를 완화하기 위해 워크로드를 전용 인스턴스로 격리합니다. 요청을 low-priority와 high-priority tier로 분할하여 별도의 인스턴스로 라우팅함으로써, low-priority 워크로드가 자원을 많이 사용하더라도 high-priority 요청의 성능에 영향을 미 미치지 않도록 합니다.
  1. Connection pooling:
    • 문제: 각 인스턴스에는 최대 연결 제한이 있으며, 연결 소진 또는 과도한 idle 연결 축적이 발생할 수 있습니다.
    • 해결책: PgBouncer를 proxy layer로 배포하여 데이터베이스 연결을 pooling합니다. statement 또는 transaction pooling 모드로 실행하여 연결을 효율적으로 재사용하고 활성 클라이언트 연결 수를 크게 줄입니다. 연결 설정 지연 시간(connection setup latency)도 50ms에서 5ms로 단축되었습니다. Network overhead를 최소화하기 위해 proxy, 클라이언트, replica를 동일 지역에 co-locate합니다. idle timeouts과 같은 PgBouncer 설정은 연결 소진 방지에 중요합니다. 각 read replica는 자체 Kubernetes deployment에서 여러 PgBouncer pod를 실행합니다.
  1. Caching:
    • 문제: 갑작스러운 cache miss 증가가 PostgreSQL에 대한 읽기 surge를 유발하여 CPU를 포화시키고 사용자 요청 속도를 늦출 수 있습니다.
    • 해결책: 대부분의 읽기 트래픽은 caching layer를 통해 처리합니다. Cache miss storm 시 과부하를 방지하기 위해 cache locking (및 leasing) 메커니즘을 구현하여 특정 key에 대한 miss가 발생했을 때 단일 reader만 PostgreSQL에서 데이터를 가져오도록 합니다. 이는 중복 데이터베이스 읽기를 크게 줄이고 시스템을 cascading load spike로부터 보호합니다.
  1. Scaling read replicas:
    • 문제: primary는 모든 read replica에 WAL(Write Ahead Log) 데이터를 스트리밍하므로, replica 수가 증가함에 따라 primary의 네트워크 대역폭 및 CPU 부담이 증가하여 replica lag이 높아지고 불안정해집니다.
    • 해결책: 현재는 매우 큰 인스턴스 유형과 높은 네트워크 대역폭으로 수십 개의 replica에 WAL을 스트리밍하는 것이 가능하지만, 무한정 replica를 추가할 수는 없습니다. Azure PostgreSQL 팀과 협력하여 cascading replication을 도입하고 있습니다. 이는 중간 replica가 WAL을 downstream replica로 전달하는 방식으로, primary에 부담을 주지 않고 수백 개의 replica로 확장할 수 있도록 합니다.
  1. Rate limit:
    • 문제: 특정 endpoint의 갑작스러운 트래픽 spike, 값비싼 쿼리의 surge, 또는 retry storm이 CPU, I/O, 연결과 같은 중요 자원을 빠르게 소진시켜 광범위한 서비스 저하를 초래할 수 있습니다.
    • 해결책: 애플리케이션, connection pooler, proxy, query 등 여러 계층에 걸쳐 rate-limiting을 구현하여 갑작스러운 트래픽 spike가 데이터베이스 인스턴스를 압도하고 cascading failure를 유발하는 것을 방지합니다. 너무 짧은 retry interval을 피하여 retry storm을 방지합니다. ORM 계층을 강화하여 rate limiting을 지원하고, 필요한 경우 특정 query digest를 완전히 차단하는 targeted load shedding을 통해 값비싼 쿼리의 갑작스러운 surge로부터 신속하게 복구할 수 있도록 합니다.
  1. Schema Management:
    • 문제: column 유형 변경과 같은 작은 schema 변경도 full table rewrite를 유발할 수 있습니다.
    • 해결책: lightweight schema 변경(예: full table rewrite를 유발하지 않는 column 추가/제거)만 허용됩니다. Schema 변경에는 strict 5초 timeout이 적용됩니다. Index 생성 및 삭제는 concurrently 허용됩니다. Schema 변경은 기존 table에 한정되며, 새로운 table이 필요한 경우 PostgreSQL 대신 Azure Cosmos DB와 같은 sharded system에 생성되어야 합니다. Table field backfill 시 write spike를 방지하기 위해 strict rate limit를 적용하여 안정성을 보장합니다.

이러한 노력의 결과, Azure PostgreSQL은 읽기 중심 워크로드에 대해 수백만 QPS를 처리하며 OpenAI의 핵심 제품을 지원하고 있습니다. 약 50개의 read replica를 추가했음에도 replication lag은 거의 0에 가깝고, 지리적으로 분산된 지역에서 낮은 지연 시간의 읽기를 유지하며, 미래 성장을 위한 충분한 용량 headroom을 확보했습니다. 지난 12개월 동안 단 한 번의 SEV-0 PostgreSQL incident만 발생했습니다. OpenAI는 shardable한 write-heavy 워크로드를 sharded system으로 마이그레이션하고 있으며, cascading replication을 통해 더 많은 read replica로 안전하게 확장할 계획입니다. 향후에는 sharded PostgreSQL 또는 다른 분산 시스템을 포함한 추가적인 확장 방안을 계속해서 모색할 것입니다.