Distributed Systems
Ceph Erasure Coding 데이터 저장 흐름
Ceph Erasure Coding 데이터 저장 흐름
1. 클라이언트 쓰기 요청 단계
- 객체 쓰기 요청: 클라이언트가 특정 풀에 데이터 쓰기 요청
- PG(Placement Group) 결정: 객체 ID를 해시하여 속할 PG 계산
- 주 OSD 선택: 해당 PG의 주(Primary) OSD로 쓰기 요청 전달
2. 데이터 인코딩 단계
-
청크 크기 계산:
- 객체 크기를 데이터 청크 수(K)로 나누어 각 청크 크기 결정
- 필요시 마지막 청크 패딩 추가
-
인코딩 요청:
encode(want_to_encode, in, &encoded);want_to_encode: 인코딩할 청크 ID 집합 (일반적으로 모든 K+M 청크)in: 원본 데이터 버퍼encoded: 인코딩된 청크들을 담을 맵
-
데이터 분할:
- 원본 데이터를 K개의 데이터 청크로 분할
- 예: 12KB 데이터, K=4인 경우 각 3KB 청크로 분할
-
코딩 청크 생성:
- 선택된 이레이저 코딩 알고리즘 실행
- 데이터 청크로부터 M개의 코딩 청크 계산
- 구현별 계산 방식:
- Jerasure: Reed-Solomon 행렬 연산
- LRC: 로컬/글로벌 패리티 계산
- ISA: 인텔 최적화 라이브러리 사용
- CLAY: 계층적 인코딩 방식
3. 청크 배치 과정
-
CRUSH 계산:
- 각 청크의 저장 위치 결정을 위해 CRUSH 알고리즘 실행
- 실패 도메인 고려 (예: 다른 호스트에 청크 배치)
-
배치 규칙 적용:
int rule_id = crush.get_rule_id(rule_name); crush.do_rule(rule_id, x, out, placement_count, weights);- 생성된 K+M개 청크를 서로 다른 OSD에 배치
- 청크 매핑 정보 저장 (각 청크가 어떤 OSD에 있는지)
-
청크 인덱스 관리:
chunk_mapping벡터에 청크 인덱스와 실제 OSD 매핑 저장- 이후 데이터 조회 시 이 매핑 정보 사용
4. 청크 저장 과정
-
병렬 쓰기 작업:
- 각 청크를 해당 OSD에 병렬로 쓰기 요청
- 데이터 청크(K개)와 코딩 청크(M개) 모두 저장
-
원자적 쓰기 보장:
- 모든 청크가 성공적으로 쓰여질 때까지 대기
- 일부 실패 시 롤백 메커니즘 활성화
-
메타데이터 업데이트:
- 객체 속성, 크기, 청크 위치 등 메타데이터 업데이트
- OMAP(Object Map)에 추가 속성 저장 가능
5. 완료 및 확인 단계
-
쿼럼 확인:
- 필요한 최소 수의 OSD가 쓰기 완료 확인
- 일반적으로 (K+M)/2 + 1 개의 확인 필요
-
클라이언트 응답:
- 쓰기 완료 확인 메시지를 클라이언트에 반환
-
백그라운드 복제:
- 일부 OSD 쓰기 실패 시 백그라운드에서 복제 시도
- 자가 복구 메커니즘 시작
6. 최적화 기법
- 청크 버퍼링: 작은 쓰기 작업을 버퍼링하여 일괄 처리
- 스트라이핑: 대용량 객체를 여러 스트라이프로 나누어 저장
- 비동기 쓰기: 성능 향상을 위한 비동기 쓰기 작업
- 로컬 패리티: LRC 코드에서 로컬 패리티로 쓰기 성능 최적화
7. 저장 효율성
- 저장 효율:
(K/(K+M)) * 100% - 예: K=8, M=4 구성에서 저장 효율은 66.7%
- 일반 복제(replication) 대비 저장 공간 절약 (3x 복제는 33.3% 효율)