🍕 1. 샤딩(Sharding): 데이터를 조각내자
서비스가 대박이 나서 사용자가 1억 명이 되었습니다. 단일 DB로는 감당이 안 됩니다. 이제 데이터를 여러 서버에 나눠 담아야 하는데, 이를 **샤딩(Sharding)**이라 합니다.
문제는 **“어떤 기준으로 나눌 것인가?”**입니다.
전략 1: Range Sharding (범위)
식별자 1 ~ 100만-> DB 1식별자 100만 ~ 200만-> DB 2- 문제점: 최근 가입한 유저만 활동한다면? DB 2만 불타오르고(Hotspot) DB 1은 놉니다.
전략 2: Modular Sharding (해시)
ID % 서버수로 배정합니다. 데이터가 아주 고르게 퍼집니다.- 치명적 문제: 서버를 3대에서 4대로 늘리면?
User ID = 3
Before (Mod 3): 3 % 3 = 0번 서버
After (Mod 4): 3 % 4 = 3번 서버
⚠️ 재앙(Rebalancing): 서버 대수가 바뀌면 거의 모든 데이터가 이동해야 합니다. 서비스 중단 없이는 불가능합니다.
Modular vs Consistent Hashing
| 특징 | Modular Hashing | Consistent Hashing |
|---|---|---|
| 규칙 | Key % N | Hash(Key)의 링 위 위치 |
| 서버 추가 시 | 전체 데이터의 약 100% 이동 | 일부 데이터 (1/N)만 이동 |
| 유연성 | 매우 낮음 (서버 수 고정 권장) | 매우 높음 (Elastic Scaling) |
| 복잡도 | 낮음 | 중간 (링 관리, 가상 노드) |
🍩 2. Consistent Hashing (일관된 해싱)
이 재앙을 막기 위해 **“서버가 추가/삭제되어도 데이터 이동을 최소화”**하는 알고리즘이 나왔습니다. 핵심은 **원형 링(Ring)**입니다.
동작 원리
graph TD
subgraph Ring ["Hash Ring (0 ~ 2^32)"]
N1((Node A: 100))
N2((Node B: 300))
N3((Node C: 600))
N1 --- N2
N2 --- N3
N3 --- N1
end
Data1[Key 1: Hash 150] -->|Clockwise ->| N2
Data2[Key 2: Hash 400] -->|Clockwise ->| N3
Data3[Key 3: Hash 800] -->|Clockwise ->| N1
style N1 fill:#ffccbc,stroke:#d84315
style N2 fill:#ffe0b2,stroke:#ef6c00
style N3 fill:#fff9c4,stroke:#fbc02d
- 커다란 원(해시 링)을 상상하세요. (0 ~ 2^32)
- **서버(Node)**를 해시값에 따라 링 위에 배치합니다.
- **데이터(Key)**도 해시값에 따라 링 위에 배치합니다.
- 데이터는 시계 방향으로 돌면서 만나는 첫 번째 서버에 저장됩니다.
서버가 추가된다면?
서버 C와 A 사이에 서버 D를 추가했습니다.
- 기존 Modular: 전체 데이터의 100%가 뒤섞임.
- Consistent Hashing: C와 D 사이의 데이터만 A에서 D로 이동. 나머지(A->B, B->C)는 그대로!
결과: 데이터 이동량이
1/N로 획기적으로 줄어듭니다.
2.2 가상 노드 (Virtual Nodes)
“운 나쁘게 Node A, B, C가 한쪽에 쏠려 있으면 어떡하죠?” (Data Skew) -> 가짜 노드를 수백 개 만들어서 링 전체에 뿌립니다.
graph TD
subgraph VirtualRing ["Virtual Nodes Spread"]
A1((A-1)) --- B1((B-1))
B1 --- C1((C-1))
C1 --- A2((A-2))
A2 --- B2((B-2))
B2 --- C2((C-2))
C2 --- A1
end
Note[Data is evenly distributed due to high variety of V-Nodes]
🆔 3. 분산 ID 생성기 (Snowflake)
샤딩을 하면 DB의 AUTO_INCREMENT를 못 씁니다. (1번 DB와 2번 DB에서 같은 ID 100번이 생기면 충돌)
전역적으로 유일한 ID가 필요합니다.
Twitter Snowflake 구조 (64bit)
packet-beta
0-15: "Sequence (12bit)\n(동일 밀리초 내 순서)"
16-25: "Machine ID (10bit)\n(서버 식별)"
26-63: "Timestamp (41bit)\n(시간순 정렬)"
| 필드 | 비트 수 | 설명 |
|---|---|---|
| Sign Bit | 1 bit | 양수 보장 (항상 0) |
| Timestamp | 41 bit | 밀리초 단위 시간 (약 69년 사용 가능) |
| Machine ID | 10 bit | 1024개의 노드 식별 가능 |
| Sequence | 12 bit | 밀리초당 4096개 ID 생성 가능 |
- Timestamp: 시간순 정렬을 보장합니다. (Index 성능에 중요)
- Machine ID: 어느 서버에서 생성했는지 구분합니다.
- Sequence: 같은 밀리초에 생성된 ID를 구분합니다.
요약
- Range Sharding: 쉽지만 핫스팟 위험.
- Modular Sharding: 균등하지만 확장 시 대이동(Rebalancing) 발생.
- Consistent Hashing: 확장이 유연함. (현대 분산 시스템 표준)
- Snowflake: 타임스탬프 기반의 유니크 ID 생성 전략.
💬 댓글