Skip to content

Commit

Permalink
Merge pull request #27 from 11th-SSAFY-19/jaeho/chapter6
Browse files Browse the repository at this point in the history
[4주차]_6장_키-값 저장소 설계_변재호
  • Loading branch information
bjho606 authored Jul 24, 2024
2 parents 1c6f51e + 9823fa7 commit f1d2b20
Showing 1 changed file with 231 additions and 0 deletions.
231 changes: 231 additions & 0 deletions 06장/[4주차]_6장_키-값 저장소 설계_변재호.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,231 @@
- key-value database : nosql
- 종류
- amazon dynamo
- memcached
- redis

| 목표/문제 | 기술 |
| --------------------------------- | ---------------------------------------- |
| 대규모 데이터 저장 | 안정 해시 ⇒ 서버 부하 분산 |
| 읽기 연산에 대한 높은 가용성 보장 | 데이터를 여러 데이터센터에 다중화 |
| 쓰기 연산에 대한 높은 가용성 보장 | 버저닝, 벡터 시계 ⇒ 충돌 해소 |
| 데이터 파티션 | 안정 해시 |
| 점진적 규모 확장 | 안정 해시 |
| 다양성 | 안정 해시 |
| 조절 가능한 데이터 일관성 | 정족수 합의 (quorum consensus) |
| 일시적 장애 처리 | 느슨한 정족수 프로토콜, 단서후 임시 위탁 |
| 영구적 장애 처리 | 머클 트리 (merkle tree) |
| 데이터 센터 장애 대응 | 데이터를 여러 데이터센터에 다중화 |

# 사전 설계 조건

1. 키-값 쌍의 크기 : 10KB 이하
2. 큰 데이터를 저장할 수 있어야함
3. 높은 가용성 - 시스템에 장애가 있더라도 빨리 응답해야함
4. 높은 규모 확장성 제공 - 트래픽 양에 따라 자동적으로 서버 증설/삭제
5. 데이터 일관성 수준 조정 가능
6. 짧은 응답 지연시간 (latency)

# 단일 서버 key-value 저장소

1. 키-값 쌍 전부 메모리에 해시 테이블로 저장
- 장점 : 빠름
- 단점 : 모든 데이터를 메모리 안에 둘 수 없음
- 개선
- 데이터 압축 (compression)
- 자주 쓰이는 데이터만 메모리에 두고, 나머지는 디스크에 저장
- but, 결국 부족해질 것!

# 분산 서버 key-value 저장소

> = `분산 해시 테이블` : 키-값 쌍을 여러 서버에 분산
## CAP 정리

> 일관성 (Consistency)
> 가용성 (Availability)
> 파티션 감내 (Partition tolerance)
- 데이터 일관성
- 분산 시스템에 접속하는 클라이언트는 어떤 노드에 접속했느냐에 관계없이 언제나 같은 데이터를 보게 되어야 한다.
- 가용성
- 분산 시스템에 접속하는 클라이언트는 일부 노드에 장애가 발생하더라도 항상 응답을 받을 수 있어야 한다.
- 파티션 감내
- 네트워크에 파티션이 생기더라도 시스템은 계속 동작하여야 한다.
- **파티션 : 두 노드 사이에 통신 장애가 발생하였음**을 의미

<aside>
💡 이 3가지 요구사항을 동시에 만족하는 분산 시스템을 설계하는 것은 불가능.
⇒ 어떤 두가지를 충족하려면, 나머지 하나는 반드시 희생되어야 함

</aside>

![6-1 cap](https://github.com/user-attachments/assets/26ab57c5-bad2-4405-8ad1-92bd2875c004)

### CP, AP, ~~CA~~ 시스템

> ex) 만약 3개의 노드 n1, n2, n3 에 데이터를 복제하여 보관하는 상황에서,
> n3에 장애가 발생하는 경우
- CP 시스템
- 가용성 (A) 희생
- 설사 낡은 데이터(n3에서 받지 못한 데이터)를 반환하더라도, 나머지 노드 (n1, n2)에서는 계속 읽기,쓰기 연산을 허용 ⇒ 파티션 문제가 해결 후, 새 데이터를 n3에 전송
- AP 시스템
- 일관성 (C) 희생
- 노드 간 데이터 불일치 문제를 피하기 위해 나머지 노드 (n1, n2)에 대해 쓰기 연산 중단
- 은행권 시스템에서는 불가능!
- 상황이 해결될 때까지 오류를 반환
- CA 시스템
- 네트워크 장애는 피할 수 없으므로, CA 시스템은 존재할 수 없음

## 시스템 컴포넌트

### 데이터 파티션

<aside>
💡 (대규모 애플리케이션에서) 데이터를 작은 파티션들로 분할한 다음, 여러 대 서버에 저장하는 것

</aside>

1. **데이터를 여러 서버에 고르게 분산할 수 있는가**
2. **노드가 추가되거나 삭제될 때, 데이터의 이동을 최소화할 수 있는가**

**안정해시**

1. 서버를 해시 링 (hash ring) 에 배치

2. 키를 같은 링 위에 배치

3. 그 지점으로부터 링을 시계 방향으로 순회하다 만나는 첫번째 서버 = 키-값 쌍을 저장할 서버

- 장점
- 규모 확장 자동화 : 시스템 부하에 따라 서버가 자동으로 추가/삭제 가능
- 다양성 : 각 서버의 용량에 맞게 가상 노드의 수 조정 가능

### 데이터 다중화 (replication)

<aside>
💡 데이터를 N개 서버에 비동기적으로 다중화하는 것 ⇒ 높은 가용성, 안정성
</aside>

- N개 서버를 선정하는 방법
1. 어떤 키를 해시 링 위에 배치

2. 그 지점으로부터 시계 방향으로 순회하면서 만나는 첫 N개 서버에 데이터 사본 보관
- 이때, N개의 노드는 서로 다른 물리 서버! (=같은 물리 서버의 가상 노드를 중복 선택하지 않도록)
- 같은 데이터 센터에 속한 노드는 정전, 네트워크 이슈, 자연재해 등의 문제를 같이 겪을 수 있으므로
- 데이터 사본은 다른 센터의 서버에 보관, 센터들은 고속 네트워크로 연결

### 일관성 (consistency)

- 정족수 합의 (Quorum Consensus) 프로토콜 ⇒ 읽기/쓰기 연산 모두 연관성 보장 가능
- N = 사본 개수
- W = 쓰기 연산에 대한 정족수
- 최소 W 개 서버로부터 쓰기 연산 성공 응답 받기 ⇒ 쓰기 연산 성공 처리
- 실제 W 개 서버에 쓰기 X, 단지 W 개 서버로부터 성공 요청 받았으면 됨
- R = 읽기 연산에 대한 정족수
- 최소 R 개 서버로부터 읽기 연산 성공 응답 받기 ⇒ 읽기 연산 성공 처리
- N, W, R 값 조절 → 요구되는 일관성 수준에 따라..
- R = 1, W = N : 빠른 읽기에 최적화
- W = 1, R = N : 빠른 쓰기에 최적화
- W + R > N : 강한 일관성 보장 (보통 N=3, W=R=2)
- W + R ≤ N : 강한 일관성 보장 X

### 일관성 모델 (consistency model)

- 데이터 일관성의 수준을 결정
- 종류
- 강한 일관성
- 모든 읽기 연산은 가장 최근에 갱신된 결과를 반환
- 클라이언트는 절대로 낡은 (out-of-date) 데이터를 보지 못함
- 모든 사본에 현재 쓰기 연산의 결과가 반영될 때까지 해당 데이터에 대한 읽기/쓰기 금지 ⇒ 고가용성 X
- 약한 일관성
- 읽기 연산은 가장 최근에 갱신된 결과를 반환하지 못할 수 있음
- 최종 일관성
- 갱신 결과가 결국에는 모든 사본에 반영 (동기화) 되는 모델
- 약한 일관성의 한 형태
- 쓰기 연산이 병렬적으로 발생하면, 데이터 일관성이 깨질 수 있음 ⇒ 클라이언트측에서 데이터 버전 정보를 활용해 해결해야 함

### 일관성 불일치 해소 (inconsistency resolution)

<aside>
데이터 버저닝 (versioning) : 데이터를 변경할 때마다 해당 데이터의 새로운 버전을 만드는 것, <br>
벡터 시계 (vector clock) : [서버, 버전]의 순서쌍을 데이터에 매단 것

</aside>

- 버저닝
- 각 버전의 데이터는 변경 불가능
- 벡터 시계
- 어떤 버전이 선행 버전인지, 후행 버전인지, 아니면 다른 버전과 충돌이 있는지 판별

![6-2 일관성 불일치 해소](https://github.com/user-attachments/assets/ed2c7237-59ac-4c71-84fb-070b707e0156)

- 두 버전 사이에 충돌 감지 ⇒ 해소
- 단점
- 충돌 감지 및 해소 로직이 클라이언트에서 처리되어야 함
- [서버:버전]의 순서쌍 개수가 굉장히 빨리 늘어남 ⇒ 길이의 임계치를 설정하고 처리해야 함

### 장애 처리

- 장애 감지
- 보통 2 대 이상의 서버가 똑같이 서버 A의 장애를 보고해야 해당 서버에 실제로 장애가 발생했다고 간주
- 종류
- 멀티캐스팅 (multicasting)
- 모든 노드 사이에 멀티캐스팅 채널을 두기
- 서버가 많아지면 비효율적
- **가십 프로토콜 (gossip protocol)** - 분산형 장애 감지 솔루션
1. 각 노드는 멤버십 목록(각 멤버id, 박동 카운터 쌍 목록)을 유지
2. 각 노드는 주기적으로 자신의 박동 카운터를 증가시킴
3. 각 노드는 무작위로 선정된 노드들에게 주기적으로 자기 박동 카운터 목록을 보냄
4. 박동 카운터 목록을 받은 노드는 멤버십 목록을 최신값으로 갱신
5. 어떤 멤버의 박동 카운터 값이 지정된 시간동안 갱신되지 않으면, 해당 멤버는 장애 상태로 간주
- 장애 처리
- 일시적 장애 처리
- 느슨한 정족수 (sloppy quorum) 접근법
1. 정족수 요구사항 (=읽기/쓰기 연산 금지) 을 강제하는 대신,
쓰기를 수행할 w개의 건강한 서버 + 읽기를 수행할 r개의 건강한 서버를 해시링에서 고르기 (장애 상태인 서버는 무시)
2. 장애 상태인 서버로 가는 요청을 다른 서버가 잠시 맡아 처리 = 임시 위탁 (hinted handoff) 기법
3. 장애 서버가 복구되었을 때, 그동안 발생한 변경사항을 일괄 반영하여 데이터 일관성 보존
- 영구 장애 처리
- 반-엔트로피(anti-entropy) 프로토콜 - 사본들을 비교하여 최신 버전으로 갱신
**머클 (Merkle) 트리**
- 데이터 센터 장애 처리
- 데이터를 여러 데이터 센터에 다중화

### 시스템 아키텍처 다이어그램

![6-3 아키텍처](https://github.com/user-attachments/assets/5f57c058-d555-4b5d-bf95-0624a9f2d98c)

- 중재자(coordinator) : 클라이언트에게 키-값 저장소에 대한 프록시 (proxy) 역할을 하는 노드
- 노드는 안정 해시의 해시 링 위에 분포
- 노드를 자동으로 추가/삭제할 수 있도록, 시스템은 완전히 분산되어 있음 (decentralized)
- 데이터는 여러 노드에 다중화
- 모든 노드가 같은 책임 ⇒ SPOF (Single Point Of Failure) 는 존재하지 않음
- 완전히 분산된 설계이므로, 모든 노드는 다음 기능들을 지원해야 함
- 클라이언트 API
- 장애 감지
- 장애 복구 메커니즘
- 데이터 충돌 해소
- 다중화
- 저장소 엔진
-

### 쓰기 경로 (write path)

![6-4 쓰기](https://github.com/user-attachments/assets/33c22a8f-119f-40eb-b72f-8456d244b9de)

1. 쓰기 요청이 커밋 로그 파일에 기록
2. 데이터가 메모리 캐시에 기록
3. 메모리 캐시가 가득차거나, 사전에 정의된 임계치에 도달 → 데이터는 디스크의 SSTable (Sorted-String Table) 에 기록
- [키, 값] 순서쌍을 정렬된 리스트 형태로 관리하는 테이블

### 읽기 경로 (read path)

![6-5 읽기](https://github.com/user-attachments/assets/17cd8c31-9412-4f2e-a813-35e134568bff)

1. 데이터가 메모리 캐시에 있는지 검사
2. 없으면, 블룸 필터를 검사
3. 블룸 필터를 통해 어떤 SSTable에 키가 보관되어 있는지 확인
4. SSTable에서 데이터 가져오기
5. 해당 데이터 클라이언트에게 반환

0 comments on commit f1d2b20

Please sign in to comment.