44P by darjeeling 2달전 | ★ favorite | 댓글 2개

요약:

  • OpenAI는 단일 Primary와 약 50개의 Read Replica(Azure Flexible Server)로 수백만 QPS와 8억 명의 사용자를 처리하고 있습니다.
  • 쓰기 부하 분산을 위해 샤딩이 가능한 워크로드는 Azure Cosmos DB로 이관하고, 애플리케이션 레벨에서 'Lazy Write' 등을 적용해 쓰기를 최적화했습니다.
  • PgBouncer를 도입해 연결 지연 시간을 50ms에서 5ms로 단축했으며, 캐시 미스 스톰을 방지하기 위한 캐시 락(Cache Locking) 메커니즘을 구현했습니다.
  • 복잡한 조인 쿼리 제거, 5초 미만의 엄격한 스키마 변경 타임아웃, 트래픽 우선순위에 따른 워크로드 격리 등을 통해 안정성을 확보했습니다.

상세요약:
1. 배경 및 아키텍처 현황
OpenAI의 PostgreSQL 트래픽은 지난 1년 동안 10배 이상 증가하여 현재 8억 명의 사용자와 수백만 QPS(초당 쿼리 수)를 처리하고 있습니다. 놀랍게도 이 규모는 단일 Primary 인스턴스와 전 세계에 분산된 약 50개의 Read Replica 구조로 운영되고 있습니다. 초기 설계의 균열을 막기 위해 OpenAI는 인프라와 애플리케이션 계층 모두에서 대대적인 최적화를 수행했습니다.

2. 주요 병목 해결 및 최적화 전략

  • 쓰기(Write) 부하 분산:

    • PostgreSQL의 단일 Writer 구조는 확장에 한계가 있고, MVCC(다중 버전 동시성 제어)로 인한 쓰기 증폭 문제가 있습니다.
    • 해결책으로 수평적 분할(Sharding)이 가능한 쓰기 집약적 워크로드는 Azure Cosmos DB와 같은 시스템으로 이관했습니다.
    • 기존 PostgreSQL에는 새로운 테이블 생성을 금지하고, 애플리케이션 버그 수정 및 Lazy Write(트래픽 스파이크를 평탄화하기 위해 쓰기를 지연 처리)를 도입하여 Primary의 부하를 최소화했습니다.
  • 쿼리 최적화 및 ORM 관리:

    • 과거 12개의 테이블을 조인하는 쿼리가 장애(SEV)를 유발한 사례가 있어, 복잡한 다중 조인을 피하고 애플리케이션 로직으로 분리했습니다.
    • ORM이 생성하는 비효율적인 쿼리를 지속적으로 검토하며, idle_in_transaction_session_timeout 설정을 통해 유휴 쿼리가 Autovacuum을 차단하지 않도록 했습니다.
  • 커넥션 풀링 (PgBouncer):

    • Azure PostgreSQL의 연결 제한(5,000개)과 연결 폭주를 방지하기 위해 PgBouncer를 프록시 레이어로 배포했습니다.
    • 트랜잭션/구문(Statement) 풀링 모드를 사용하여 연결 재사용성을 높였고, 평균 연결 시간을 50ms에서 5ms로 단축했습니다.
  • 캐시 미스 방지 (Cache Locking):

    • 캐시가 만료되었을 때 수많은 요청이 동시에 DB로 몰리는 'Thundering Herd' 문제를 방지하기 위해 캐시 락(Leasing) 메커니즘을 도입했습니다.
    • 특정 키에 대해 캐시 미스가 발생하면, 단 하나의 요청만 DB에 접근 권한(Lock)을 얻어 데이터를 갱신하고, 나머지 요청은 대기하도록 하여 DB 부하를 차단했습니다.

3. 안정성 및 운영 정책

  • 단일 실패 지점(SPOF) 완화: Primary 장애 시에도 읽기 요청은 Replica를 통해 처리되도록 하여 장애 등급을 낮췄으며, Primary는 고가용성(HA) 모드로 Hot Standby를 유지해 빠른 페일오버를 보장합니다.
  • 워크로드 격리: 'Noisy Neighbor' 문제를 방지하기 위해 트래픽을 중요도(Low/High Priority)에 따라 분리된 인스턴스로 라우팅합니다.
  • 엄격한 스키마 관리: 테이블 전체 재작성(Full Table Rewrite)을 유발하는 변경은 금지되며, 스키마 변경 시 5초의 엄격한 타임아웃을 적용해 서비스 지연을 방지합니다.

4. 향후 계획 (The Road Ahead)
현재 구조로도 충분한 확장성을 확보했지만, 향후 더 많은 Read Replica 확장을 위해 Primary가 모든 Replica에 WAL을 전송하는 구조 대신, 중간 Replica가 하위로 WAL을 전달하는 Cascading Replication을 테스트 중입니다. 장기적으로는 PostgreSQL 자체의 샤딩도 고려하고 있습니다.

Hacker News 토론 요약: https://news.ycombinator.com/item?id=46725300

단일 인스턴스의 위엄: 8억 명 규모의 트래픽을 샤딩 없이 단일 Postgres(Write)로 처리한다는 점에 대해 "역시 거대한 DB 서버는 우리의 친구(Big DB servers are your friend)"라며 수직 확장(Vertical Scaling)의 유효성을 재확인하는 반응이 많습니다.

샤딩 vs 리팩토링의 아이러니: 본문 중 "기존 앱 리팩토링이 너무 복잡해서 샤딩을 선택하지 않았다"는 대목에 대해, "코딩 AI를 파는 회사가 리팩토링이 어려워서 못 한다는 게 아이러니하다"는 뼈 있는 농담과 비판이 오갔습니다. (한편으론 샤딩이 가져올 운영 복잡도나 마이그레이션 비용을 고려할 때 합리적인 선택이라는 옹호도 있습니다.)

기술적 깊이에 대한 아쉬움: 글이 다소 일반적인 내용(캐싱, 커넥션 풀링 등) 위주라, 구체적인 엔지니어링 디테일이 부족해 "홍보용 글 같다"는 비판적인 시각도 일부 존재합니다.

Rust 관련 논의: 댓글 중에는 본문과 별개로 Rust의 컴파일 타임 체크를 이용해 'Idle Transaction' 문제를 원천 차단하는 기법을 공유하며 기술적인 토론이 깊게 이어지기도 했습니다.

저는 개인적으로 Cascading Replication 같은 구조를 적용하거나 운영으로 기술적 한계를 돌파한 부분이 흥미로웠습니다. 관련해서 제 생각은 페북에 좀 더 길게 정리해 두었습니다. https://www.facebook.com/share/p/1Kp8V917bL/