참조 아키텍처: 대규모 / 고트래픽
트래픽이 증가할 때 데이터베이스를 보호하는 올바른 순서가 있다: 먼저 단순하고 저렴한 수단, 그다음 복잡한 수단. 이 트리는 수직 확장 → 읽기 복제본 → 샤딩 순서, 캐싱 패턴(cache-aside, write-back), 큐를 이용한 버퍼링 + backpressure, rate limiting, bulkhead/circuit breaker를 통한 데이터베이스 격리, 핫스팟 방지를 단계별로 다룬다. 핵심 원칙: 샤딩처럼 되돌리기 어려운 결정은 마지막으로 미루고 저렴한 개선을 먼저 소진한다.
병목은 어디인가: 단일 인스턴스가 포화되었는가, 읽기나 쓰기가 많은가, 아니면 트래픽이 급증하는가?
확장 문제를 올바른 곳에서 해결하려면 먼저 병목을 식별한다. 단일 인스턴스가 CPU/메모리에서 포화되었는가, 읽기가 지배적인가, 쓰기가 한계에 이르렀는가, 아니면 갑작스러운 트래픽 급증이 데이터베이스를 압도하는가? 각각 다른 구성 요소를 가리킨다.
- 단일 인스턴스가 포화됨, 아직 단순한 수단을 시도하지 않음
- 읽기가 매우 많고 증가 중
- 갑작스러운 트래픽 급증 / 폭발적 부하가 DB를 압도함
어느 확장 단계에 있는가: 수직, 읽기 복제본, 샤딩?
확장에는 올바른 순서가 있다. 수직 확장(더 강력한 하드웨어)이 가장 단순한 첫 단계이고, 그다음 읽기를 복제본에 분산하며, 단일 primary가 더 이상 쓰기를 감당하지 못할 때만 수평 분할(샤딩)로 넘어간다. 지금 어디에 있는가?
- 아직 하드웨어를 증설하지 않았거나 기본 튜닝을 소진하지 않음
- 쓰기는 단일 인스턴스로 충분하지만 읽기 부하를 분산해야 함
- 단일 primary가 더 이상 쓰기 부하를 감당하지 못함
데이터베이스를 부하로부터 어떻게 보호할 것인가: 버퍼링, 제한, 격리?
확장이 항상 DB를 키우는 것은 아니다. 많은 경우 DB를 과부하로부터 보호하는 것이다. 갑작스러운 쓰기 급증을 큐로 평탄화할 것인가, 들어오는 요청 속도를 제한할 것인가(rate limiting), 아니면 단일 의존성이 시스템 전체를 다운시키지 않도록 할 것인가(bulkhead/circuit breaker)?
- 갑작스러운 쓰기 급증을 평탄화하고 싶음
- 들어오는 요청 속도를 제한/공정하게 분배하고 싶음
- 장애가 난 하나의 의존성이 시스템 전체를 다운시키지 않기를 원함
권장: 수직 확장과 기본 튜닝 우선
가장 저렴하고 위험이 적은 단계는 수직 확장이다: CPU/RAM/더 빠른 디스크를 추가하고 기본 사항(인덱스, 커넥션 풀링, 메모리 파라미터)을 바로잡는다. 샤딩 같은 복잡한 결정 전에 이 개선들을 소진한다.
권장: 읽기 복제본으로 읽기 분산
쓰기는 단일 primary에 유지하고 읽기 전용 트래픽은 하나 이상의 복제본에 분산한다. 샤딩보다 훨씬 단순하며 대부분의 읽기 중심 워크로드를 해결한다. primary는 쓰기와 일관성이 핵심인 읽기만 담당한다.
권장: 최후 수단으로서의 샤딩(수평 분할)
단일 primary가 쓰기 부하를 감당하지 못하면 데이터를 키 기준으로 여러 독립 조각(샤드)으로 분할한다. 각 샤드가 쓰기의 일부를 담당한다. 강력하지만 되돌리기 어렵고, 샤드 간 쿼리와 트랜잭션을 복잡하게 하므로 마지막으로 미룬다.
권장: 캐싱 패턴(cache-aside + write-back)
읽기 중심 고트래픽에서는 DB 앞단에 인메모리 캐시(Redis)를 두어 대부분의 읽기를 DB에 도달하지 않고 처리한다. 대량 쓰기에서는 write-back이 쓰기를 버퍼링해 DB에 배치로 반영하므로 primary 부담이 줄어든다.
권장: 큐를 이용한 버퍼링(backpressure)
갑작스러운 쓰기 급증이 DB를 직접 강타하지 않도록 중간에 내구성 있는 큐(예: Kafka/메시지 큐)를 두고 DB가 감당할 수 있는 속도로 쓰기를 소비한다. 큐가 급증을 흡수하며, 시스템은 동기적으로 실패하는 대신 통제된 지연으로 동작한다(backpressure).
권장: rate limiting(요청 속도 제한)
출처별로 들어오는 요청을 제한하면(rate limiting) 남용과 단일 클라이언트의 리소스 독점을 모두 방지한다. DB에 도달하는 부하에 상한을 두어 시스템을 예측 가능하고 공정하게 유지한다.
권장: bulkhead + circuit breaker(격리)
의존성(예: 느려진 DB나 외부 서비스)이 시스템 전체를 다운시키지 않도록 리소스를 구획(bulkhead)으로 나누고, 계속 실패하는 의존성에 대한 호출을 일시적으로 차단한다(circuit breaker). 이렇게 하면 부분 장애가 시스템 전체로 번지지 않는다.