[Daily morning study] 샤딩(Sharding)과 파티셔닝 전략

#daily morning study

Image


샤딩(Sharding)과 파티셔닝 전략

파티셔닝(Partitioning)

파티셔닝은 하나의 테이블을 여러 조각으로 나누는 기법이다. 하나의 물리적 DB 서버 안에서 테이블을 분할하기 때문에 수직 확장(Scale-Up)의 범위 안에 있다.

수평 파티셔닝 (Horizontal Partitioning)

행(row) 기준으로 데이터를 나눈다. 같은 스키마를 가진 테이블 여러 개에 데이터가 분산된다.

users 테이블 원본 (1억 행)

→ users_0 (id 0~33백만)
→ users_1 (id 33~66백만)
→ users_2 (id 66~100백만)

각 파티션의 스키마는 동일하고, 쿼리는 파티션 키를 기준으로 적절한 파티션에만 접근한다.

수직 파티셔닝 (Vertical Partitioning)

열(column) 기준으로 테이블을 쪼갠다. 자주 조회되는 컬럼과 그렇지 않은 컬럼을 분리해서 I/O 효율을 높인다.

users 테이블
  id, name, email, profile_image, bio, settings, ...

→ users_core    : id, name, email
→ users_profile : id, profile_image, bio
→ users_settings: id, settings

JOIN이 필요해지지만 자주 쓰는 컬럼만 읽기 때문에 캐시 효율이 좋다.


샤딩(Sharding)

샤딩은 수평 파티셔닝을 여러 DB 서버에 걸쳐 적용한 개념이다. 데이터를 샤드(Shard) 단위로 쪼개고 각 샤드를 별도 서버(노드)에 배치한다. 수평 확장(Scale-Out)의 핵심 기법이다.

Application
    │
    ▼
Shard Router (어떤 샤드로 보낼지 결정)
    │
    ├──▶ Shard 0 (DB Server A)
    ├──▶ Shard 1 (DB Server B)
    └──▶ Shard 2 (DB Server C)

샤드 라우터(또는 프록시)가 샤드 키를 보고 어느 서버로 쿼리를 보낼지 결정한다.


샤딩 전략

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

샤드 키의 값 범위에 따라 샤드를 배정한다.

샤드범위
Shard 0user_id 1 ~ 1,000,000
Shard 1user_id 1,000,001 ~ 2,000,000
Shard 2user_id 2,000,001 ~

장점: 범위 쿼리(BETWEEN, >, <)가 특정 샤드에 집중되어 효율적이다.

단점: 데이터가 고르게 분산되지 않을 수 있다. 신규 가입이 몰리면 최신 범위 샤드에만 부하가 집중된다(Hot Spot).

2. 해시 기반 샤딩 (Hash-Based Sharding)

샤드 키에 해시 함수를 적용해서 샤드 번호를 결정한다.

shard_id = hash(user_id) % num_shards

장점: 데이터가 균등하게 분산된다. Hot Spot 문제가 줄어든다.

단점: 범위 쿼리가 어렵다. 샤드 수를 바꾸면 기존 데이터를 대규모로 재배분(Resharding)해야 한다.

3. 디렉토리 기반 샤딩 (Directory-Based Sharding)

별도의 룩업 테이블(디렉토리)을 두고, 각 키가 어느 샤드에 있는지 직접 관리한다.

Lookup Table
user_id → shard
1001    → Shard 0
1002    → Shard 2
1003    → Shard 1

장점: 샤드 배치를 유연하게 변경할 수 있다.

단점: 룩업 테이블이 단일 장애점(SPOF)이 될 수 있다. 매번 룩업 테이블을 조회해야 해서 레이턴시가 증가한다.

4. 일관된 해싱 (Consistent Hashing)

해시 공간을 원형 링(Ring)으로 구성하고 각 노드를 링 위에 배치한다. 데이터는 해시값이 가장 가까운 노드에 저장된다.

        Node A
       /      \
Node D        Node B
       \      /
        Node C

노드를 추가하거나 제거할 때 전체 데이터를 재배분하지 않고 인접 구간의 데이터만 이동한다. Cassandra, DynamoDB 등 분산 시스템에서 자주 쓰인다.


파티셔닝 vs 샤딩 비교

구분파티셔닝샤딩
분산 범위단일 DB 서버 내여러 DB 서버에 분산
확장 방식수직 확장수평 확장
트랜잭션로컬 트랜잭션 가능분산 트랜잭션 필요
관리 복잡도낮음높음
적합한 규모수천만 ~ 수억 행수억 행 이상, 대용량 트래픽

샤딩의 단점과 주의사항

크로스 샤드 쿼리 문제
여러 샤드에 걸친 JOIN이나 집계 쿼리는 각 샤드에서 데이터를 가져와 애플리케이션 레이어에서 병합해야 한다. 복잡도가 크게 올라간다.

분산 트랜잭션
2개 이상의 샤드에 걸친 트랜잭션은 2PC(Two-Phase Commit)나 Saga 패턴으로 처리해야 하는데, 구현이 복잡하고 성능 오버헤드가 있다.

Resharding
데이터가 늘어나 샤드를 추가해야 할 때 기존 데이터 재배분이 필요하다. 해시 기반이라면 일관된 해싱으로 이동량을 최소화할 수 있다.

샤드 키 선택
샤드 키는 데이터 분산의 핵심이다. 카디널리티(Cardinality)가 높고 고르게 분포되는 값을 골라야 Hot Spot을 피할 수 있다. 한번 결정하면 바꾸기 매우 어렵다.


실전 적용 사례

MySQL Vitess (YouTube, Shopify)
MySQL 위에서 샤딩을 추상화해주는 미들웨어다. 샤드 라우팅, 스키마 변경, Resharding을 자동으로 처리한다.

MongoDB
내장된 샤딩 기능을 제공한다. mongos가 라우터 역할을 하고 Config Server가 샤드 메타데이터를 관리한다.

PostgreSQL 파티셔닝
PostgreSQL 10+부터 선언적 파티셔닝(Declarative Partitioning)을 지원한다. Range, List, Hash 파티셔닝을 기본으로 제공한다.

-- PostgreSQL 범위 파티셔닝 예시
CREATE TABLE orders (
    id BIGINT,
    created_at DATE,
    amount NUMERIC
) PARTITION BY RANGE (created_at);

CREATE TABLE orders_2025 PARTITION OF orders
    FOR VALUES FROM ('2025-01-01') TO ('2026-01-01');

CREATE TABLE orders_2026 PARTITION OF orders
    FOR VALUES FROM ('2026-01-01') TO ('2027-01-01');

요약

  • 파티셔닝: 단일 서버 내에서 테이블을 분할. 관리가 쉽고 트랜잭션 유지 가능.
  • 샤딩: 여러 서버에 데이터를 분산. 수평 확장 가능하지만 크로스 샤드 쿼리와 분산 트랜잭션이 복잡해진다.
  • 샤드 키 선택이 성능과 확장성을 좌우한다. Hot Spot을 피하기 위해 고카디널리티 컬럼을 선택해야 한다.
  • 서비스 초기에는 파티셔닝으로 충분하고, 단일 서버 한계에 도달하면 샤딩을 고려한다.