read-replicas

1 개의 포스트

figma

데이터베이스 아키텍처 (새 탭에서 열림)

Figma는 사용자와 기능 증가로 단일 PostgreSQL 데이터베이스의 CPU 사용률과 지연 시간이 급증하자, 장애 위험을 선제적으로 낮추기 위해 멀티 데이터베이스 구조로 전환했다. 단기적으로 인스턴스 확장, 읽기 복제본, PgBouncer 등을 도입했지만 쓰기 부하와 복제 지연 문제는 해결하지 못했다. 수평 샤딩이나 NoSQL 전환 대신, 연관된 테이블 그룹을 별도 데이터베이스로 옮기는 수직 파티셔닝을 장기 해법으로 선택했다. ## 단일 데이터베이스의 확장 한계 - Figma는 권한, 파일 정보, 댓글 등 대부분의 메타데이터를 하나의 대형 Amazon RDS 데이터베이스에 저장했다. - 데이터베이스 트래픽은 매년 약 3배씩 증가했다. - 2020년 피크 시간대 CPU 사용률이 65%를 넘었고, 사용량이 한계에 가까워질수록 지연 시간이 예측하기 어려워졌다. - 데이터베이스가 완전히 포화되면 Figma 전체가 작동을 멈출 수 있었기 때문에, 장애가 발생하기 전에 확장 문제를 해결할 필요가 있었다. ## 단기적인 안정화 조치 Figma는 장기적인 구조 변경을 준비하는 동안 약 1년의 추가 여유를 확보하기 위해 다음 조치를 시행했다. - RDS 인스턴스를 `r5.12xlarge`에서 `r5.24xlarge`로 확장해 CPU 처리 여력을 늘렸다. - 여러 개의 read replica를 구성해 읽기 트래픽을 분산했다. - 새로운 사용 사례에는 별도 데이터베이스를 사용해 기존 데이터베이스의 성장을 제한했다. - PostgreSQL 연결 풀러인 PgBouncer를 도입해 수천 개에 달하는 데이터베이스 연결이 주 데이터베이스에 미치는 영향을 줄였다. - PgBouncer는 애플리케이션과 RDS 사이에서 연결을 관리하고 재사용하는 계층으로 동작했다. ## 읽기 복제본만으로는 부족했던 이유 - 데이터베이스 사용량을 분석한 결과, 데이터 수집·수정·삭제와 같은 쓰기 작업도 상당한 부하를 차지했다. - 모든 읽기 요청을 replica로 옮길 수 없었다. - 일부 기능은 복제 지연(replication lag)에 민감해 최신 데이터가 반드시 주 데이터베이스에서 제공되어야 했다. - 따라서 읽기와 쓰기 양쪽 모두에서 원본 데이터베이스의 작업을 추가로 분산해야 했다. ## 수평 확장 방안의 검토 Figma는 테이블을 여러 데이터베이스에 분산하는 수평 샤딩도 검토했지만, 다음과 같은 부담이 있었다. - Figma가 사용하는 PostgreSQL과 기본적으로 호환되지 않는 관리형 수평 확장 솔루션이 많았다. - NoSQL이나 MySQL 기반 Vitess로 이전하려면 애플리케이션에 큰 변경이 필요했다. - 마이그레이션 과정에서 이중 읽기·쓰기 구조를 운영해야 하므로 복잡성이 커졌다. - PostgreSQL 호환 NewSQL을 선택하면 클라우드 환경에서 매우 큰 분산 PostgreSQL 클러스터를 운영하는 초기 고객이 될 위험이 있었다. - 관리형 솔루션은 내부 동작과 확장 한계를 통제하기 어렵고, Figma 규모에서 충분히 검증되지 않은 문제를 직접 겪을 수 있었다. - 직접 호스팅하면 운영 지식과 인력을 새로 확보해야 하며, 상당한 운영 비용이 발생했다. ## 테이블 그룹 단위의 수직 파티셔닝 Figma는 수평 샤딩 대신 수직 파티셔닝을 선택했다. - 수직 파티셔닝은 테이블 또는 컬럼을 다른 데이터베이스로 이동하는 방식이다. - Figma는 개별 테이블의 행을 여러 데이터베이스로 쪼개는 대신, 서로 관련된 테이블 그룹 전체를 별도 데이터베이스로 옮겼다. - 이 방식은 기존 데이터베이스의 부하를 즉시 줄일 수 있었다. - 장기적으로는 특정 테이블 그룹에 대해 향후 수평 샤딩을 적용할 수 있는 기반도 제공했다. - 기존 PostgreSQL 생태계와 애플리케이션 구조를 크게 바꾸지 않고 확장할 수 있다는 점이 장점이었다. ## 분할 대상 선정 기준 데이터베이스를 분리하기 전에 어떤 테이블을 옮길지 평가해야 했다. 주요 기준은 다음 두 가지였다. - **영향도(Impact)** - 테이블을 이동했을 때 데이터베이스 작업량이 크게 줄어야 했다. - Figma는 쿼리에 대한 평균 활성 세션 수(AAS)를 사용해 부하를 측정했다. - PostgreSQL의 `pg_stat_activity`를 10밀리초 간격으로 조회해 쿼리별 활성 상태와 CPU 대기 관련 정보를 분석했다. - **격리성(Isolation)** - 분리 대상 테이블이 다른 테이블과 강하게 결합되어 있지 않아야 했다. - 테이블 간 의존성이 높으면 데이터베이스 간 조인과 트랜잭션 처리가 복잡해지므로, 독립적으로 이동할 수 있는 영역을 우선했다. 결국 Figma의 접근 방식은 단일 데이터베이스를 무리하게 확장하는 대신, 부하가 크고 다른 기능과의 결합도가 낮은 테이블 그룹부터 별도 데이터베이스로 분리하는 것이었다. 실무에서도 먼저 읽기 부하뿐 아니라 쓰기 부하와 복제 지연을 함께 측정하고, 수평 샤딩보다 운영 복잡성이 낮은 수직 분할부터 검토하는 것이 현실적인 전략이다.