Skip to content

Commit 16b4f8b

Browse files
authored
Create 3주차 김현준.md
1 parent 6200b70 commit 16b4f8b

File tree

1 file changed

+228
-0
lines changed

1 file changed

+228
-0
lines changed

week3(6~7장)/김현준.md

Lines changed: 228 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,228 @@
1+
# 5장 안정 해시 설계
2+
3+
## 안정 해시
4+
5+
- 해시 테이블 크기가 조정될 때 평균적으로 오직 k/n개의 키만 재배치하는 해시 기술.
6+
- k는 키의 개수, n은 슬롯의 개수
7+
- 해시 테이블 크기가 조정될 때 → 서버 증설 및 서버 장애 시
8+
- 전통적 해시 테이블은 슬롯의 수가 바뀌면 거의 대부분 키를 재배치함
9+
10+
### 해시링
11+
12+
- 해시 공간을 원형으로 만들어 사용
13+
- 해시링을 사용하면 안정 해시를 구현할 수 있음
14+
- 일부만 재배치 가능 → 서버 추가 시 해당 서버와 반 시계 방향에서 처음 만나는 서버 사이에 있는 키를 재배치함
15+
- 서버와 키를 균등 분포 해시 함수를 사용해 해시 링에 배치
16+
- 키의 위치에서 링을 시계 방향으로 탐색하다 만나는 최초의 서버가 키가 저장될 서버
17+
- 서버가 추가되거나 삭제되는 상황을 감안하면 파티션의 크기를 균등하게 유지하는게 불가능
18+
- 파티션은 인접한 서버 사이의 해시 공간
19+
- 키의 균등 분포를 달성하기 어려움
20+
- 가상 노드 또는 복제로 불리는 기법 활용해 해결 가능
21+
22+
### 가상 노드
23+
24+
- 실제 노드 또는 서버를 가리키는 노드
25+
- 하나의 서버는 링 위에 여러 개의 가상 노드를 가질 수 있음
26+
- 따라서 하나의 서버가 여러개의 파티션(해시공간)을 관리해야 함
27+
- 가상 노드의 개수를 늘리면 키의 분포는 더 균등해짐 → 표준 편차가 작아짐(데이터가 퍼져 나갔는지 보이는 척도)
28+
- 대신 가상 노드 데이터를 저장할 공간이 더 많이 필요
29+
30+
# 6장 키-값 저장소 설계
31+
32+
### 단일 서버
33+
34+
- 키-값 쌍 전부를 메모리에 해시 테이블로 저장
35+
- 모든 데이터를 메모리 안에 두는 것이 불가능할 수도 있다는 약점
36+
- 데이터 압축
37+
- 자주 쓰는 데이터만 두고 나머지는 디스크에 저장
38+
39+
## 분산 키-값 저장소
40+
41+
- 분산 해시 테이블
42+
43+
### CAP 정리
44+
45+
- 데이터 일관성(Consistency)
46+
- 어떤 노드에 접속하든 언제나 같은 데이터를 보게 되어야 함
47+
- 가용성(Availability)
48+
- 일부 노드 장애 발생 시에도 응답 받을 수 있어야 함
49+
- 파티션 감내(Partition tolerance)
50+
- 파티션
51+
- 두 노드 사이의 통신 장애
52+
- 파티션이 생기더라도 시스템은 계속 동작하여야 함
53+
- CAP에선 두 가지를 충족하려면 나머지 하나는 희생되어야 함
54+
- CA: 일관성과 가용성 지원 → 통신 장애는 피할 수 없으므로 존재하지 않는 시스템
55+
56+
### 문제 상황
57+
58+
하나의 노드가 기록 후 장애로 인해 다른 노드로 데이터를 전달하지 못했을 경우, 다른 노드는 오래된 사본을 가지게 된다.
59+
60+
- 가용성 대신 일관성을 선택하면 데이터 불일치 문제를 피하기 위해 쓰기 연산을 중단하고 해결 될 때 까지 오류 반환
61+
- 일관성 대신 가용성을 선택하면 낡은 데이터를 반환하더라고 읽기 연산 허용, 쓰기 연산도 허용하고 파티션 문제가 해결된 뒤에 새 데이터를 전송
62+
63+
### 데이터 파티션
64+
65+
- 데이터를 작은 파티션으로 분할한 다음 서버에 저장
66+
- 데이터를 고르게 분산시키고 노드 추가 및 삭제 시 데이터 이동을 최소화 해야함
67+
- 안정 해시 사용 !
68+
69+
### 데이터 다중화
70+
71+
- 데이터를 N개 서버에 비동기적으로 다중화할 필요가 있다.
72+
- 해시 링 위에 배치 후 시계 방향으로 순환하며 N개의 서버에 사본 보관
73+
- 가상 노드를 사용하면 N개의 노드가 대응될 실제 물리 서버의 개수가 N보다 작아질 수 있음
74+
- 하나의 서버가 여러 개의 파티션을 관리하기 때문 ! (같은 서버가 관리하는 노드를 여러 개 만날 수 있다)
75+
- 같은 물리 서버를 중복 선택하지 않도록 유의 → 같은 데이터 센터도 유의 !
76+
77+
### 데이터 일관성
78+
79+
정족수 합의 프로토콜(Quorum Consensus)을 이용해 적절히 데이터를 동기화
80+
81+
- N = 사본 개수
82+
- W = 쓰기 연산에 대한 정족수. 쓰기 연산이 성공한 것으로 간주되면 적어도 W개의 서버로부터 쓰기 연산이 성공했다는 응답을 받아야 한다.
83+
- R = 읽기 연산에 대한 정족수. 읽기 연산이 성공한 것으로 간주되려면 적어도 R개의 서버로부터 응답을 받아야 한다.
84+
85+
중재자(클라이언트와 노드 사이의 Proxy)가 필요하다. W, R, N 값을 정하는 것은 응답 지연과 데이터 일관성 사이의 타협점을 찾는 것이다.
86+
87+
W, R = 1 인 경우 한 대의 서버로 중재자가 응답 받으면 되니까 응답 시간이 빠르지만 데이터 일관성 수준을 보장할 수 없다. 값을 키우면 응답 시간이 느려진다.
88+
89+
- R = 1, W = N : 빠른 읽기 연산에 최적화된 시스템
90+
- W = 1, R = N : 빠른 쓰기 연산에 최적화된 시스템
91+
- W + R > N : 강한 일관성이 보장됨
92+
- W + R ≤ N : 강한 일관성이 보장되지 않음
93+
94+
### 일관성 모델
95+
96+
- 강한 일관성 : 모든 읽기 연산은 가장 최근에 갱신된 결과를 반환한다. 클라이언트는 예전 데이터를 보지 못함
97+
- 일반적으로 모든 사본에 쓰기 결과가 반영될 때까지 해당 데이터에 대한 읽기/쓰기를 금지
98+
- 약한 일관성 : 읽기 연산은 가장 최근에 갱신된 결과를 반환하지 못할 수 있음
99+
- 최종 일관성 : 약한 일관성의 한 형태로 갱신 결과가 ‘결국에는’ 모든 사본에 동기화 되는 모델
100+
- 쓰기 연산이 병렬적으로 발생할 경우 일관성이 깨짐
101+
- 클라이언트가 이 문제를 해결해야 함
102+
103+
### 비 일관성 해소 기법 : 데이터 버저닝
104+
105+
버저닝은 데이터 변경 시 해당 데이터의 새로운 버전을 만드는 것을 의미하고 각 버전의 데이터는 불변이다.
106+
107+
버저닝의 충돌 해결 방법은 벡터 시계라는 방법이다.
108+
109+
- 벡터 시계 = (서버, 버전) 정보를 데이터에 매단 것
110+
- 이를 통해 충돌 판단 → 버전으로 먼저 바꾼 것인지, 후에 바꾼 것인지 판단 가능
111+
- 충돌 감지 및 해소가 클라이언트에 들어가 클라이언트 구현이 복잡해짐
112+
- (서버, 버전) 정보가 빨리 늘어나 임계치 도달 시 오래된 벡터 시계를 지워야 함
113+
114+
### 장애 감지
115+
116+
- 모든 노드 사이에 멀티캐스팅을 구축해 장애 감지
117+
- 서버가 많으면 많은 통신이 일어나 비효율적
118+
- 가십 프로토콜(gossip) 등을 사용해 분산형 장애 감지
119+
- 각 노드는 멤버십 목록을 유지. 멤버십 목록은 각 멤버 ID와 heartbeat 카운터
120+
- 각 노드는 주기적으로 자신의 박동 카운터를 증가
121+
- 각 노드는 무작위로 선정된 노드들에게 주기적으로 자기의 카운터 목록을 보냄
122+
- 목록을 받은 노드는 멤버십 목록을 최신 값으로 갱신
123+
- 만약 어떤 멤버가 일정 시간 동안 갱신되지 않으면 장애 상태인 것으로 간주
124+
- 이를 통해 최소한의 통신으로 장애 판단 가능
125+
126+
### 장애 처리
127+
128+
- 일시적 장애 처리
129+
- 엄격한 정족수 사용 시 읽기 쓰기 연산 금지
130+
- 느슨한 정족수 사용 시 가용성을 높임
131+
- 쓰기 연산을 수행할 W개의 건강한 서버와 읽기 연산을 수행할 R개의 건강한 서버를 해시 링에서 고름
132+
- 장애 상태인 서버로 가는 요청을 다른 서버가 잠시 맡아 처리하고 해당 서버 복구 시 일괄 반영
133+
- 임의로 처리한 서버에는 힌트를 남겨둔다.
134+
- 영구 장애 처리
135+
- 반 엔트로피 프로토콜 구현해 사본을 동기화
136+
- 머클 트리(해시 트리)를 사용
137+
- 루트 노드의 해시 값이 일치하면 두 서버의 데이터가 같다.
138+
- 따라서 이 것을 이용해 트리를 탐색하면서 다른 데이터를 갖는 버킷을 찾아 그 버킷들만 동기화한다.
139+
140+
## 머클 트리(해시 트리)
141+
142+
### **머클 트리의 구조**
143+
144+
머클 트리는 **이진 트리** 형태로 구성되며, 다음과 같은 노드들로 구성됩니다:
145+
146+
1. **리프 노드 (Leaf Node)**:
147+
- 원본 데이터의 해시 값이 저장되는 노드입니다.
148+
- 예: 데이터를 SHA-256과 같은 해시 함수로 변환한 값.
149+
2. **중간 노드 (Intermediate Node)**:
150+
- 자식 노드들의 해시 값을 결합하여 다시 해시한 값이 저장됩니다.
151+
- 예: 왼쪽 자식 해시와 오른쪽 자식 해시를 연결한 뒤 이를 해시화.
152+
3. **루트 노드 (Root Node)**:
153+
- 트리의 최상단 노드로, 전체 데이터의 해시 정보를 나타냅니다.
154+
- 이 루트 해시 값은 데이터 전체의 무결성을 검증하는 데 사용됩니다.
155+
156+
### **머클 트리의 생성 과정**
157+
158+
1. **데이터 준비**:
159+
- 데이터를 작은 조각(블록)으로 나눕니다.
160+
2. **리프 노드 해싱**:
161+
- 각 데이터 블록에 대해 해시 값을 계산하여 리프 노드를 만듭니다.
162+
3. **중간 노드 생성**:
163+
- 리프 노드들의 해시 값을 두 개씩 묶어 해시를 계산하여 부모 노드를 생성.
164+
- 이 과정을 반복하여 트리의 루트 노드까지 계산.
165+
4. **루트 노드 생성**:
166+
- 최종적으로 하나의 루트 해시 값이 남습니다.
167+
168+
### **머클 트리의 특징**
169+
170+
1. **효율적인 검증**:
171+
- 전체 데이터를 비교하지 않고, 특정 데이터 조각의 변경 여부를 확인할 수 있습니다.
172+
- 검증에는 루트 해시와 해당 데이터 조각의 해시값, 관련 노드의 해시 값만 필요합니다.
173+
2. **무결성 보장**:
174+
- 데이터가 변경되면, 해당 리프 노드와 관련된 부모 노드, 루트 노드까지 영향을 미칩니다.
175+
- 루트 해시를 통해 데이터가 변경되었는지 쉽게 확인 가능합니다.
176+
3. **저장 효율성**:
177+
- 해시 값만 저장하기 때문에 큰 데이터를 처리할 때 저장 공간이 절약됩니다.
178+
4. **분산 시스템에 적합**:
179+
- 데이터 조각을 여러 노드에 분산 저장하더라도, 루트 해시로 전체 데이터를 검증할 수 있습니다.
180+
181+
## SSTable과 블룸 필터
182+
183+
### LSM 트리와 SSTable
184+
185+
- LSM 트리는 데이터를 메모리에 유지하다가, 일정 크기 이상이 되면 데이터를 디스크로 플러시함
186+
- 이때 디스크에 생성되는 것이 SSTable
187+
- 각 로그 구조화 저장소 세그먼트는 키-값 쌍의 연속이며 순차적으로 데이터가 생성되므로 나중의 값이 가장 최신의 값임
188+
- 저장소 세그먼트를 키로 정렬한 형식을 **정렬된 문자열 테이블(Sorted String Table, SS테이블)**이라 부름
189+
- 각 키는 병합된 세그먼트 파일 내에 1번만 나타나야 함
190+
- 찾고자 하는 키가 존재할 범위를 예상하고 해당 영역에서만 값을 조회하여 찾을 수 있음.
191+
- merge sort 알고리즘 사용 → 모든 키의 인덱스를 관리할 필요 없음(부분 배열로 나누어 머지하기 때문)
192+
193+
![image.png](https://prod-files-secure.s3.us-west-2.amazonaws.com/1d4134fe-30e0-4ca4-8bdc-d0da96f2bbeb/526bfdf7-1788-4f5f-b02c-565c0c768c55/image.png)
194+
195+
- 예를 들어 handiwork 키를 찾으려면 handbag과 handsome 키의 오프셋을 알고 정렬 되어 있어 handiwork는 두 키 사이에 있음을 알 수 있음.
196+
- handbag 오프셋으로 이동해 handiwork가 나올 때 까지 스캔하면 됨
197+
- 일부 키에 대한 오프셋을 알려주는 인덱스 테이블이 필요하지만 일부만 관리하면 됨
198+
- 읽기 요청은 요청 범위 내 여러 키-값 쌍을 스캔해야 하므로 해당 레코드들을 블록으로 그룹화, 디스크에 쓰기 전에 압축함
199+
- 그러면 인덱스 테이블에 각 항목은 압축된 블록의 시작을 가리키게 되고 디스크 공간 절약 및 I/O 대역폭 사용 감소라는 장점이 생김
200+
201+
### SS테이블 생성과 유지
202+
203+
- 쓰기 요청이 들어오면 인메모리 균형트리 데이터 구조에 추가한다.
204+
- 이런 인메모리 트리는 Memtable이라고도 한다.
205+
- 새로운 SS테이블 파일은 DB에서 가장 최신 세그먼트가 된다.
206+
- SS테이블을 디스크에 기록하는 동안 쓰기는 새로운 멤테이블 인스턴스에 기록한다.
207+
- 읽기 요청을 제공하려면 먼저 멤테이블에서 키를 찾는다.
208+
- 그 다음 디스크 상의 가장 최신 세그먼트 -> 두 번째 오래된 세그먼트 -> N 번째 세그먼트 순서로 찾는다.
209+
- 그리고 중간중간 백그라운드로 병합과 컴팩션 과정을 수행한다.
210+
- DB 장애 시 아직 디스크에 기록되지 않고 멤테이블에 있는 가장 최신의 쓰기 작업 데이터는 손실
211+
- 매번 쓰기를 즉시 추가할 수 있게 분리된 로그를 디스크 상에 유지(WAL → 쓰기 요청 들어오면 WAL에 기록 후 응답)
212+
- 멤테이블 복원 시에만 필요해 순서가 정렬되지 않아도 되며 멤테이블을 SS테이블로 기록하고 나면 디스크에 저장된 데이터 삭제
213+
- SS테이블 = 디스크 상에 정렬된 세그먼트 파일
214+
215+
### 성능 최적화
216+
217+
- LSM 트리 알고리즘은 DB에 존재하지 않는 키를 찾는 경우 느릴 수 있음
218+
- 존재하지 않는 키를 찾는 경우 가장 오래된 세그먼트까지 거슬러 올라가야함
219+
- 블룸 필터를 추가로 사용해 DB에 키가 존재하지 않음을 알려줘서 불필요한 디스크 읽기 줄임
220+
- 블룸 필터는 원소가 집합에 속하는지 여부를 검사하는 확률적 자료구조
221+
- 원소가 집합에 속한다고 판단된 경우 원소가 집합에 속하지 않는 긍정 오류 발생 가능
222+
- 그 반대인 부정 오류는 절대 발생하지 않음 (속하지 않음 → 사실 속하는 거 였던 거임 ㅋㅋ)
223+
224+
출처 https://www.scylladb.com/glossary/sstable/
225+
226+
https://goodgid.github.io/SD-Repository-and-Search-SS-table-and-LSM-tree/
227+
228+
https://ko.wikipedia.org/wiki/%EB%B8%94%EB%A3%B8_%ED%95%84%ED%84%B0

0 commit comments

Comments
 (0)