Pink Spider/DB Shading

Created Tue, 10 Jun 2025 08:44:58 +0900 Modified Mon, 08 Dec 2025 08:41:47 +0900
3489 Words 16 min

데이터베이스(DB) 샤딩(DB Sharding)이란 대용량 데이터를 효율적으로 분산·관리하기 위해 하나의 거대한 데이터베이스를 여러 개의 작은 조각(샤드, shard)으로 나누어 각각을 별도의 서버에 분산 저장하고 처리하는 기법을 말합니다.


1. 왜 샤딩이 필요한가?

  • 성능 확장성(Scalability)

    • 단일 서버에 모든 데이터를 저장하면 저장 용량·처리 능력이 한계에 부딪힙니다.
    • 데이터와 트래픽이 늘어날수록 로드가 집중되어 응답 지연(latency)이나 병목(bottleneck)이 발생합니다.
  • 고가용성(High Availability)

    • 각 샤드를 독립된 노드에 두면, 일부 노드 장애 시에도 전체 시스템이 완전히 다운되지 않고 가용성을 유지할 수 있습니다.
  • 관리 용이성

    • 데이터 규모가 커질수록 백업·복구·마이그레이션 같은 운영 작업이 어려워지는데, 샤딩으로 작은 단위로 나누면 관리 부담을 줄일 수 있습니다.

2. 샤딩 방식

  1. 범위 기반 샤딩(Range Sharding)

    • 특정 컬럼(예: 사용자 ID나 날짜)을 기준으로 값의 범위별로 샤드를 나눕니다.
    • 예: ID 1–1,000 → 샤드 A, 1,001–2,000 → 샤드 B
  2. 해시 기반 샤딩(Hash Sharding)

    • 샤드 키(보통 프라이머리 키)에 해시 함수를 적용해 결과값을 샤드 수로 나눈 나머지로 저장 위치를 결정합니다.
    • 분포가 균일해지는 장점이 있으나, 범위 검색 시 모든 샤드를 조회해야 할 수 있습니다.
  3. 디렉터리 기반 샤딩(Directory Sharding)

    • 중앙에 샤드 매핑 테이블을 두어, 각 레코드가 어느 샤드에 있는지 직접 매핑 관리합니다.
    • 유연하지만 매핑 테이블이 병목이 될 수 있습니다.

3. 장단점

구분 장점 단점
성능 읽기·쓰기 부하를 여러 노드로 분산 → 처리량 증가 샤딩 키 설계 실패 시 불균형(Hot Spot) 발생 위험
가용성 일부 노드만 장애 → 시스템 전체는 계속 운영 가능 네트워크 분산 환경 → 트랜잭션 관리(분산 트랜잭션)가 복잡해짐
확장성 샤드 추가만으로 수평적 확장(horizontal scaling) 가능 샤드 추가·삭제 시 데이터 재분배(Rebalancing) 비용 발생
운영 복잡도 모니터링·백업·백오프 등 운영 복잡도 증가

4. 설계 시 고려사항

  1. 샤드 키 선정

    • 조회·갱신 패턴을 분석해 자주 함께 조회되는 데이터가 같은 샤드에 있도록 설계
  2. 데이터 균형 분산(Balancing)

    • 샤드 간 데이터 크기·트래픽 부하가 최대한 균등하도록 조정
  3. 분산 트랜잭션 처리

    • 애플리케이션에서 분산 트랜잭션을 최소화하거나, Saga 패턴·2PC 등 적절한 분산 커밋 방식 적용
  4. 장애 복구 전략

    • 각 샤드 백업 주기, 복제(replica) 설정, 페일오버(failover) 정책 수립

요약

DB 샤딩은 대규모 데이터를 수평적으로 분산 저장해 성능과 가용성을 높이는 필수 설계 기법입니다. 하지만 잘못된 샤드 키나 불충분한 운영·모니터링 없이 도입하면 오히려 복잡도와 위험만 늘어나므로, 사전 분석충분한 테스트가 꼭 필요합니다.

실무적으로 DB 샤딩을 도입·운영하려면 크게 다음과 같은 단계와 고려사항을 거쳐야 합니다.


1. 요구사항 분석 및 전략 수립

  1. 비즈니스·데이터 특성 파악

    • 하루 읽기·쓰기 요청량, 데이터 성장 추이, 피크 트래픽 패턴 등을 수집
    • 어떤 테이블(혹은 컬렉션)에 부하가 집중되는지 확인
  2. 샤딩 목표 정리

    • 쓰기 처리량 증대 vs. 읽기 처리량 증대 vs. 대용량 저장소 확장 중 우선순위 결정
    • 장애 허용 범위 및 복제(Replication) 전략 수립

2. 샤드 키(Sharding Key) 설계

  • 핫스팟(Hot Spot) 방지

    • 균등 분산을 위해 요청 분포가 고르게 퍼지는 컬럼을 선택
    • 예: 해시 기반 샤딩이라면 user_id % N, 범위 샤딩이라면 가입일자와 같이 분산될 수 있는 값
  • 쿼리 패턴 고려

    • 자주 함께 조회·조인되는 데이터는 같은 샤드에 두도록 설계(예: 사용자 프로필과 주문 내역)
  • 확장성 확보

    • 향후 샤드 노드 추가 시 최소한의 데이터 이동으로도 리밸런싱이 가능하도록(Consistent Hashing 등)

3. 샤딩 아키텍처 구성

  1. 샤드 노드 구성

    • 물리 서버 또는 클라우드 인스턴스에 데이터베이스 인스턴스를 배포
    • 각 샤드는 독립된 Master–Slave(또는 Primary–Replica) 구성으로 장애 대책 마련
  2. 라우팅 계층(Routing Layer)

    • 애플리케이션 내 샤드 키 기반 분기 로직 구현

      // 예시: Java에서 MySQL 샤딩
      int shardCount = 4;
      int shardId = userId % shardCount;
      DataSource ds = dataSources.get(shardId);
      
    • 또는 ProxySQL, HAProxy, Mycat, Vitess 같은 샤딩 프록시 솔루션 도입

  3. 메타데이터 관리

    • 샤드별 위치, 범위, 상태 정보를 담은 메타테이블 또는 ZooKeeper·etcd 같은 분산 키–값 저장소에 관리

4. 데이터 마이그레이션 & 초기 로드

  • 신규 서비스라면

    • 애플리케이션 배포 전 미리 샤드별로 빈 테이블 생성
    • 초기 데이터 로드 스크립트를 병렬 실행
  • 기존 단일 DB에서 이전할 경우

    1. 오프라인 마이그레이션: 서비스 중단이 허용될 때 일괄 추출·삽입

    2. 온라인 마이그레이션:

      • CDC(Change Data Capture) 도구(Debezium, Maxwell’s Daemon 등)로 실시간 복제
      • 점진적으로 트래픽을 샤드 클러스터로 전환
      • 최종 Cut-over 시점에 레플리카 싱크를 확인 후 스위칭

5. 분산 트랜잭션 & 쿼리 처리

  • 트랜잭션 단위 최소화

    • 가능하면 단일 샤드 내에서 처리가능하도록 서비스 로직 재구성
  • 분산 트랜잭션

    • 꼭 필요할 때만 Saga 패턴(보상 트랜잭션)·Two-Phase Commit 사용
  • 조인·집계 쿼리

    • 샤드별 로컬 집계를 수행한 뒤 애플리케이션 레벨에서 합산
    • Spark, Presto 등 분산 쿼리 엔진 연동 검토

6. 모니터링·운영

  • 샤드 상태·성능 모니터링

    • 각 샤드의 CPU·메모리·디스크 I/O, 네트워크 지표 수집(Prometheus + Grafana 등)
    • 샤드별 쿼리 지연(latency)·QPS·Lock Wait 지표 감시
  • 자동 리밸런싱

    • Consistent Hashing 기반 솔루션 사용 시 노드 추가·삭제 시 자동 균형 조정
    • 수동 리밸런싱일 경우, 오프라인 스크립트 또는 온라인 툴(pt-reshard for Percona 등) 활용
  • 백업·복구 정책

    • 샤드별 스냅샷(S3, GCS)·WAL(Write-Ahead Log) 보관
    • 장애 발생 시 특정 샤드만 복구 가능하도록 절차 문서화

7. 대표적 오픈소스·상용 솔루션

종류 예시 특징
프록시 기반 ProxySQL, Mycat, Vitess 애플리케이션 수정 최소, 프록시에서 라우팅 관리
DB 내장 샤딩 MongoDB, Cassandra, Citus DB 자체에서 샤딩·복제 관리, 운영 복잡도 다소 높음
미들웨어·플랫폼 Sharding-Sphere, Apache Trafodion 다양한 DB 지원, 복잡한 분산 트랜잭션 지원

실무 팁

  • 테스트 환경 구축: 실제 운영과 동일한 샤드 구성으로 부하 테스트·장애 시나리오 연습
  • 점진적 적용: 우선 부하가 가장 높은 몇 개 테이블만 부분 샤딩 후, 점차 확장
  • 문서화: 샤드 키, 라우팅 로직, 장애 복구 절차 등 기술 문서를 반드시 유지

이처럼 DB 샤딩은 단순 분산만이 아닌, 설계 ⇒ 구현 ⇒ 마이그레이션 ⇒ 운영 전 과정을 종합적으로 관리해야 하며, 특히 샤드 키 선정과 장애 대비 전략이 성패를 좌우합니다. 회사를 비롯한 실제 서비스 환경에서는 위 가이드라인을 팀·서비스 특성에 맞춰 세부 조정을 거치는 것이 중요합니다.

샤딩을 구현할 때 “테이블 템플릿(구조)은 동일하게 유지하되, 물리적으로는 여러 개의 스키마나 데이터베이스에 똑같은 테이블을 복제해서 분산 저장”하는 방식이 많이 쓰입니다. 다만 실제로는 스키마 단위보다는 데이터베이스 인스턴스(혹은 노드) 단위로 나누는 게 보편적이고, 그 아래에 각각의 스키마(혹은 네임스페이스)를 두는 형태로 운영합니다.


1. 스키마 기반 샤딩(또는 스키마 별 분할)의 개념

  • 같은 테이블 설계(DDL)를 유지하면서,
  • 샤드 키(예: user_id % N 등)에 따라 각각 다른 스키마(또는 데이터베이스)로 라우팅
  • 각 스키마에는 동일한 테이블명이지만 서로 다른 데이터 범위만 저장
샤드 수 N = 3일 때
shard_0 스키마 → users 테이블: user_id % 3 == 0
shard_1 스키마 → users 테이블: user_id % 3 == 1
shard_2 스키마 → users 테이블: user_id % 3 == 2

이렇게 하면 애플리케이션 레이어나 샤딩 프록시가 “‘users’ 테이블에 접근하라”는 쿼리를 샤드 키로 분석해 적절한 스키마·노드로 분기해 줍니다.


2. 스키마 단위 대신 데이터베이스(인스턴스) 단위 운영이 많은 이유

  1. 격리(Isolation)

    • 같은 데이터베이스 인스턴스 내 여러 스키마는 자원(CPU, 메모리, I/O)을 공유 → 노드 장애 시 전체 스키마 영향
    • 반면, 인스턴스 단위 분할은 물리적·논리적으로 완전 격리 가능
  2. 운영 편의성

    • 백업·복제·장애조치(Failover) 설정을 인스턴스 단위로 관리
    • 모니터링·알림 체계도 인스턴스별로 분리해 구성
  3. 확장성

    • 샤드 추가·삭제 시 새로운 DB 인스턴스만 늘리거나 줄이면 되고, 스키마 단위보다 리밸런싱을 더 수월

3. 스키마 기반 샤딩이 유용할 때

  • 테스트·개발 환경에서 샤드 구성을 모사할 때
  • 초기 PoC로 간단히 분산 효과만 확인하고 싶을 때
  • 동일 인스턴스 내 자원 활용이 충분하고, 완전한 장애 격리가 필요 없을 때

하지만 운영 환경에서는 보통 “샤드당 별도 DB 인스턴스 + 동일한 스키마 템플릿” 형태로 구성한 뒤, Mycat, Vitess, ProxySQL 같은 미들웨어나 애플리케이션 레이어에서 라우팅하는 방식을 주로 채택합니다.


요약

  • 테이블 구조는 동일하게, 물리적 저장소를 스키마별(혹은 DB 인스턴스별)로 분리하는 게 기본 개념
  • 운영 환경에선 스키마 단위보다는 별도 데이터베이스 인스턴스 단위로 샤드를 나누는 경우가 일반적
  • 스키마 기반 샤딩은 소규모·테스트용, 또는 내부 리소스 활용이 충분한 경우에만 권장

이처럼 “같은 구조 테이블을 여러 스키마에 분산”하는 건 샤딩의 한 형태지만, 실제 운영에서는 완전한 격리와 확장성을 위해 샤드별 별도 DB 인스턴스 + 동일 스키마 템플릿 조합을 더 많이 사용합니다.