DB 복제(Replication)와 샤딩

한 줄 정의

Replication은 데이터를 여러 서버에 복제하여 읽기를 분산하고 가용성을 높이는 기법이고, Sharding은 데이터를 여러 서버에 분할 저장하여 쓰기와 저장 용량을 분산하는 기법이다.

실무에서 왜 중요한가

  • 단일 DB의 읽기/쓰기 한계를 넘는 트래픽에서 어떻게 확장하는지 설명해야 한다.
  • Replication 지연(lag)을 모르면 “방금 저장한 데이터가 안 보인다”는 버그를 만든다.
  • Sharding 키를 잘못 선택하면 특정 샤드에 데이터가 몰려서 효과가 없다.
  • 확장 전략을 모르면 단일 DB 한계에서 서비스 장애로 이어진다.

Replication (복제)

동작 원리

        쓰기 (INSERT/UPDATE/DELETE)
Client ─────────────────────────────→ Master (Primary)
                                        │
                                        │ binlog 복제
                                        ▼
                                    Replica 1 (읽기 전용)
                                    Replica 2 (읽기 전용)
                                        ▲
Client ─────────────────────────────────┘
        읽기 (SELECT)
구성 요소역할
Master (Primary)모든 쓰기 처리, binlog 생성
Replica (Secondary)Master의 binlog를 받아 재실행, 읽기 처리

MySQL Replication 흐름

  1. Master에서 데이터 변경 시 binlog에 기록
  2. Replica의 IO Thread가 binlog를 가져와 relay log에 저장
  3. Replica의 SQL Thread가 relay log를 재실행하여 데이터 동기화

Replication Lag (복제 지연)

시간 ──→
Master:  INSERT 완료 ─────────────────────────
Replica:              ───── lag ─── INSERT 반영
                      ↑
                      이 시점에 Replica를 읽으면 데이터가 없음

문제 상황: 사용자가 글을 작성한 직후 목록을 조회하면 방금 쓴 글이 안 보인다.

대응 전략:

전략방법적용
쓰기 후 읽기는 Master방금 쓴 사용자의 조회는 Master로 라우팅가장 흔함
강제 동기화 대기semi-synchronous replication정합성 중요 시
버전 체크쓰기 시점의 binlog position과 Replica position 비교복잡하지만 정밀

Spring에서 읽기/쓰기 분리

@Transactional(readOnly = true)  // Replica로 라우팅
public List<Order> getOrders() { ... }
 
@Transactional  // Master로 라우팅
public void createOrder() { ... }

AbstractRoutingDataSource로 트랜잭션의 readOnly 속성에 따라 DataSource를 분기한다.

Sharding (분할)

동작 원리

Client 요청 (user_id = 12345)
       │
       ▼
  Shard Router (user_id % 4 = 1)
       │
  ┌────┼────┬────┐
  ▼    ▼    ▼    ▼
Shard0 Shard1 Shard2 Shard3
       ↑
   여기에 저장

데이터를 Shard Key 기준으로 여러 DB에 분산 저장한다.

Sharding 전략

전략방법장단점
Hash Shardingshard = hash(key) % N균등 분산, 샤드 추가 시 재배치 필요
Range Sharding0~999 → Shard0, 1000~1999 → Shard1범위 조회 유리, 핫스팟 발생 가능
Directory Sharding별도 라우팅 테이블로 관리유연하지만 라우팅 테이블이 SPOF

Shard Key 선택 기준

좋은 Shard Key의 조건:

  1. 균등 분산: 특정 샤드에 데이터가 몰리지 않아야 한다.
  2. 쿼리 라우팅 가능: 대부분의 쿼리에 Shard Key가 포함되어야 한다.
  3. 변경되지 않음: 키가 변경되면 데이터를 다른 샤드로 이동해야 한다.
예시적합도이유
user_id좋음대부분의 쿼리가 사용자 기준, 균등 분산
created_date나쁨최신 데이터에 쓰기가 집중 (핫스팟)
country주의국가별 데이터량 차이가 크면 편향 발생

Sharding의 어려움

문제설명
Cross-shard JOIN여러 샤드에 걸친 JOIN이 불가하거나 매우 비쌈
Cross-shard 트랜잭션분산 트랜잭션 필요, 성능과 복잡도 증가
샤드 추가/리밸런싱데이터 이동이 필요, 서비스 영향
Global unique IDAUTO_INCREMENT 불가, 별도 ID 생성 전략 필요 (Snowflake 등)
집계 쿼리모든 샤드를 조회하고 합쳐야 함

Replication vs Sharding

구분ReplicationSharding
목적읽기 분산, 가용성쓰기 분산, 저장 용량 확장
데이터전체 복사분할 저장
확장 대상읽기 처리량읽기 + 쓰기 처리량
복잡도낮음높음
도입 시점읽기 부하가 높을 때단일 DB 용량/쓰기 한계 도달 시

확장 순서 (일반적)

1. 단일 DB + 인덱스/쿼리 최적화
2. 캐시(Redis) 도입
3. Read Replica 추가 (Replication)
4. 테이블 분리 (수직 분할)
5. Sharding (수평 분할) ← 최후의 수단

자주 나는 실수

  • Replication lag을 고려하지 않고 쓰기 직후 Replica에서 읽어서 정합성 문제가 발생한다.
  • Shard Key를 시간 기반으로 잡아서 최신 샤드에 부하가 집중된다.
  • 필요하지 않은 시점에 Sharding을 도입해서 복잡도만 늘어난다.
  • Cross-shard 쿼리가 많은 설계에서 Sharding을 적용해서 성능이 더 나빠진다.

핵심 요약

Replication은 데이터를 복제해서 읽기를 분산하고 가용성을 높입니다. 복제 지연(lag)이 있으므로, 쓰기 직후 읽기는 Master로 라우팅하는 전략이 필요합니다.

Sharding은 데이터를 분할 저장하여 쓰기와 용량을 분산합니다. Shard Key는 균등 분산 + 쿼리에 항상 포함 + 변경 불가를 만족해야 합니다.

확장은 인덱스 최적화 → 캐시 → Replication → Sharding 순서로 검토하며, Sharding은 복잡도가 매우 높으므로 최후에 도입합니다.

꼬리 질문

관련 문서