Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion ch05-consistent-hashing/week1.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ SHA-1 기준 해시 공간은 `0 ~ 2¹⁶⁰-1` 의 범위를 가진다.

| | 일반 해시 | 안정 해시 |
| --- | --- | --- |
| **서버 결정 방식**
`serverIndex` | hash(key) % N | 링에서 hash(key) 기준 시계 방향 첫 번째 서버 |
| **저장 공간** | 가변: 0 ~ N-1 | 고정: 0 ~ 2¹⁶⁰-1 |
| **서버 변경 시** | 저장 공간 전체 재정의 → 대부분 키 영향 | 저장 공간 고정 → 근처 키만 영향 |
Expand Down Expand Up @@ -130,6 +129,11 @@ Strategy 2에 아래 조건을 추가했다.
- 부하가 가장 균등해짐 (Strategy 1/2/3 중 로드밸런싱 효율 1위)
- 각 노드가 유지해야 할 정보가 Strategy 1 대비 **1000배 감소**

## 2주차에 보완해야할 점
- Strategy3을 최종 채택했을 때, 서버별 스펙이 다른 경우 유연한 설계가 불가하다.
- Stratedy3 + 유연성을 설계한데도 더 좋은 방법이 있을 수 있다.
- 메타데이터의 트레이드오프를 고려했을때, 가상노드 개수는 어떻게 설정해야하나?

## 참고 자료

- [Amazon Dynamo 논문 원문 (Cornell CS5414)](https://www.cs.cornell.edu/courses/cs5414/2017fa/papers/dynamo.pdf)
Expand Down
181 changes: 180 additions & 1 deletion ch06-key-value-store/week2.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,4 +84,183 @@ Q. 근데 중재자가 선택되는 방식이 단순 첫번째로 고정되는
| 쓰기 가용성 | 별도 항목 없음 | 낮은 W(quorum)로 일부 응답만으로 성공 처리 |
| 요청 처리 흐름 | 명시 안 됨 | 코디네이터(중재자) 개념 도입, GET/PUT 단계별 동작 정의 |
| 데이터 일관성이 깨질 수 있는 시나리오 | "모르겠음" | 벡터 시계 비교 + R/W quorum(R+W>N) 조합으로 해결됨을 확인 |
| 장애 복구 방식 | "재분배" (모호) | 일시/영구 장애 구분, 영구 장애 시 머클 트리로 차이만 동기화 |
| 장애 복구 방식 | "재분배" (모호) | 일시/영구 장애 구분, 영구 장애 시 머클 트리로 차이만 동기화 |

---

## 5. 확장성 설계 구체화: 안정 해시 + 가상노드 실체화

1주차/2주차에서 "안정 해시 + 가상노드 도입"이라고 적었던 부분을 실제 동작 수준으로 풀어낸다.

### 해시 링 구현

링은 **정렬된 맵(TreeMap)**으로 구현한다.

```
Key : 가상노드의 해시값 (SHA-1 기준 0 ~ 2¹⁶⁰-1)
Value : 해당 가상노드가 속한 실제 서버 ID
```

서버 등록 시 V개의 가상노드를 생성한다:

```
hash("서버A#0"), hash("서버A#1"), ..., hash("서버A#V-1")
```

**키 → 서버 조회**: `hash(key)` 이상의 가장 가까운 값을 TreeMap에서 ceiling 탐색 → O(log(N×V))

### 서버 스펙별 가상노드 수 차등 부여

서버마다 스펙이 다른 경우, 가상노드 수를 가중치에 비례하게 설정한다. (ch05 week2 참고)

```
서버별 가상노드 수 = 기본_V × (서버_가중치 / 평균_가중치)
```

이 방식으로 **균등 파티션 분배(Strategy 3) + 서버 스펙 반영(유연성)** 을 동시에 달성한다.

### 서버 추가/삭제 시 재배치 범위

**서버 추가**
- 새 서버의 V개 가상노드를 링에 삽입
- 각 삽입 지점 기준으로 반시계 방향 이전 가상노드까지의 범위 중 일부만 이전

**서버 삭제 / 장애**
- 해당 서버의 V개 가상노드를 링에서 제거
- 각 제거 지점의 시계 방향 다음 서버가 해당 범위를 인수
- V개로 분산되어 있으므로 부하가 단일 서버가 아닌 **여러 서버로 고르게** 나뉨

### 복제(Replication)와의 관계

N개 복제를 위해 링에서 시계 방향으로 N개의 **실제 서버**를 찾아야 한다.

가상노드 사용 시 시계 방향으로 N개를 찾으면 같은 물리 서버가 중복될 수 있어 중복 제거가 필요하다.

```
복제 대상 서버 목록 = []
링에서 hash(key)부터 시계 방향 순회:
if 현재 가상노드의 실제 서버가 목록에 없으면:
목록에 추가
if 목록 크기 == N:
종료
```

또한 복제 서버는 가능하면 **물리적으로 다른 랙/데이터센터**에 있어야 한다.
→ 가상노드 순회 시 랙 정보도 체크해서 같은 랙이면 건너뛰는 방식으로 구현 가능

### 코디네이터 서버 선택

GET/PUT 동작에서 "중재자(첫 번째 서버)"가 코디네이터 역할을 한다.

- 링에서 `hash(key)` 기준 시계 방향 첫 번째 실제 서버 = 코디네이터
- 코디네이터가 나머지 N-1개 복제 서버에 데이터를 전파
- 코디네이터 자체도 데이터를 갖는 N개 중 하나

### 링 메타데이터 관리

해시 링 정보는 모든 서버가 동일하게 알아야 한다.

| 방식 | 특징 | 단점 |
| --- | --- | --- |
| 중앙 설정 서버 (ZooKeeper 등) | 일관성 보장, 구현 단순 | SPOF 위험, 추가 인프라 필요 |
| 가십 프로토콜 (분산 동기화) | SPOF 없음, Amazon Dynamo 방식 | 동기화 지연으로 일시적 불일치 가능 |

**채택: 가십 프로토콜**
- 각 서버는 주기적으로 일부 다른 서버에 현재 멤버십 정보를 전파
- 서버 추가/삭제 이벤트가 전체 클러스터에 퍼지는 데 수 초~수십 초 소요
- 이 기간 동안 잘못된 서버로 라우팅될 수 있음 → 해당 서버가 데이터를 모르면 올바른 서버로 포워딩하거나 클라이언트가 재시도

---

## 6. Fixed Hash Slot 방식과 비교

ch05 week2에서 확장성 요구사항을 만족하는 실무 방식으로 Virtual Node와 Fixed Hash Slot 두 가지를 다뤘다. 이 키-값 저장소 설계에 각각 적용했을 때 어떻게 동작하는지 비교한다.

### Fixed Hash Slot 적용 시

해시 공간을 고정된 16,384개 슬롯으로 나누고, 각 서버가 연속된 슬롯 범위를 담당한다.

**슬롯-서버 매핑 예시**
```
슬롯 0 ~ 5460 → 서버 A
슬롯 5461 ~ 10922 → 서버 B
슬롯 10923 ~ 16383 → 서버 C
```

**키 → 슬롯 조회**: `slot = CRC16(key) % 16384` → O(1)

**서버 추가 시**
- 기존 서버들에서 슬롯 일부를 새 서버로 명시적 이전
- 이전 중인 슬롯의 키 요청은 MOVED 응답으로 올바른 서버로 리다이렉트

**서버 삭제 시**
- 해당 서버의 슬롯을 다른 서버로 모두 이전 완료 후 제거

**복제 구현**: 슬롯 범위 단위로 master-replica 구조 지정

### Virtual Node vs Fixed Hash Slot 비교

| 항목 | Virtual Node (Dynamo) | Fixed Hash Slot (Redis) |
| --- | --- | --- |
| 키 → 서버 조회 | O(log N×V), TreeMap ceiling 탐색 | O(1), CRC16 % 16384 |
| 서버 추가/삭제 | 링에서 가상노드 즉시 삽입/제거 | 슬롯 이전 후 추가/제거 |
| 재배치 자동화 | ✅ 자동 | 조율 필요 (명시적 이전) |
| N개 복제 구현 | 링 순회로 N개 실제 서버 탐색 | 슬롯 범위 단위 master-replica |
| 이기종 서버 가중치 | ✅ 가상노드 수 조절 | ✅ 슬롯 수 조절 |
| 슬롯 이전 중 가용성 | 일시 불일치 가능 | MOVED 리다이렉트로 처리 |
| 운영 복잡도 | 높음 | 낮음 |

### 이 설계에서의 선택: Virtual Node

- **자동 스케일링**: 링에 가상노드를 삽입/제거하는 것만으로 재배치가 자동 처리됨. Fixed Hash Slot은 슬롯 이전을 별도로 조율해야 해서 자동화가 더 복잡함
- **N개 복제 + 가십 프로토콜 조합**: 링 순회로 복제 서버를 동적으로 탐색하는 방식이 가십 기반 멤버십 관리와 자연스럽게 맞음. Fixed Hash Slot은 슬롯 범위 단위 복제 구조가 고정되어 있어 유연성이 낮음
- **이기종 서버**: 둘 다 지원하지만 Virtual Node는 가상노드 수 하나만 조절하면 됨

---

## 7. 범위 기반 파티셔닝과 비교

ch05 week2에서 해시 공간 없이도 동작하는 세 번째 방식으로 범위 기반 파티셔닝을 다뤘다. 이 키-값 저장소 설계에 적용했을 때 어떻게 동작하는지 비교한다.

### 범위 기반 파티셔닝 적용 시

키를 해시하지 않고 키 값의 정렬 순서로 공간을 나눠 서버에 배분한다.

**범위-서버 매핑 예시**
```
"a" ~ "h" → 서버 A
"i" ~ "p" → 서버 B
"q" ~ "z" → 서버 C
```

**키 → 서버 조회**: 중앙 메타데이터 서버에서 키가 속하는 범위를 조회 → 서버 결정

**서버 추가 시**
- 부하가 높은 범위를 분할해 새 서버에 일부 이전
- 메타데이터 서버가 분할 결과를 반영

**서버 삭제 시**
- 해당 서버의 범위를 인접 서버에 병합
- 메타데이터 서버가 병합 결과를 반영

**핫스팟 자동 대응**: 특정 범위에 요청이 집중되면 그 범위를 더 잘게 분할해 여러 서버로 분산

**복제 구현**: 각 범위에 N개 복제본을 메타데이터 서버가 직접 관리

### 세 방식 최종 비교

| 항목 | Virtual Node (Dynamo) | Fixed Hash Slot (Redis) | Range-based (HBase 등) |
| --- | --- | --- | --- |
| 키 → 서버 조회 | 로컬 계산, O(log N×V) | 로컬 계산, O(1) | 메타데이터 서버 조회 필요 |
| 중앙 라우팅 서버 | ❌ 불필요 | ❌ 불필요 | ✅ 필요 |
| SPOF 위험 | 없음 | 없음 | 메타데이터 서버가 SPOF |
| 서버 추가/삭제 | 링 삽입/제거 (자동) | 슬롯 이전 (조율 필요) | 범위 분할/병합 (자동 가능) |
| 핫스팟 자동 분산 | 제한적 (가상노드 수 조절) | 제한적 (슬롯 수 조절) | ✅ 범위 자동 분할 |
| 범위 쿼리 지원 | ❌ 해시로 순서 파괴 | ❌ 해시로 순서 파괴 | ✅ 자연스럽게 지원 |
| 운영 복잡도 | 중간 | 낮음 | 높음 |

### 이 설계에서 범위 기반을 선택하지 않는 이유

- 이 키-값 저장소의 키는 순서 의미가 없는 임의 키이므로 범위 쿼리 지원 이점이 없음
- 중앙 메타데이터 서버가 생기면 SPOF가 되어 가용성 요구사항에 위배
- 메타데이터 서버 조회가 매 요청마다 추가되면 지연시간 요구사항에 불리함