1. 요구사항 정리
•
블록 저장소 = 디스크 드라이브를 물리적으로 연결한 형태
◦
원시 블록을 서버에 볼륨 형태로 제공한다.
◦
가장 유연나고 융통성이 높은 저장소
◦
데이터베이스나 가상 머신 엔진 같은 애플리케이션은 원시 블록을 직접 제어하여 최대한의 성능을 끌어냄
◦
서버 입장에서 보면 네트워크/물리적으로 연결된 저장소 모두 동일하게 동작한다.
•
파일 저장소 = 블록 저장소 위에 구현되는 레이어
◦
파일/디렉터리를 손쉽게 다루기 위한 추상화를 제공
◦
데이터는 계층적으로 구성되는 디렉터리 안에 보관됨
◦
가장 널리 사용되는 범용 저장소 솔루션
•
객체 저장소 = 실시간 갱신에 필요없는 콜드 데이터 보관에 초점을 맞추어 아카이브나 백업에 주로 쓰인다.
◦
모든 데이터는 수평적 구조 내에 객체로 보관됨
◦
다른 유형의 저장소에 비해 상대적으로 느림
1.1. 모든 요구사항
•
버킹 생성
•
객체 업로드 및 다운로드
•
객체 버전
•
버킷 내 객체 목록 출력 기능
•
매년 100PB의 데이터가 추가 가능해야 함
•
큰 객체든 작은 객체든 효율적으로 저장해야 함
•
99.999%의 데이터 내구성을 보장
•
99.99%의 서비스 가용성을 보장
1.2. 규모 추정
•
디스크 용량: 객체 크기가 다음 분포를 따른다고 가정하자.
◦
객체 가운데 20%는 그 크기가 1MB 미만의 작은 객체다.
◦
정도의 객체는 1MB-64MB 정도 크기의 중간 크기 객체다.
◦
나머지 20% 정도는 64MB 이상의 대형 객체다.
•
lOPS(IO per seconds)
◦
하드 디스크 하나가 초당 100~150회의 임의 데이터 탐색을 지원할 수 있다고 가정 (100~ 150 IOPS)
•
계산의 편의를 위해서 객체 유형별 중앙값 사용
◦
소형 = 0.5MB, 중형 = 32MB, 대형 = 200MB
100PB = 100 X 1000 X 1000 X 1000MB = 10^11MB
(10^11 X 0.4) / (0.2 X 0.5MB + 0.6 X 32MB + 0.2 X 200MB) = 6억 8천만 개
Plain Text
복사
2. 청사진 그리기
•
객체 저장소의 몇 가지 속성
◦
객체 저장소에 저장되는 객체는 수정이 불가능하다.
▪
삭제 후 새버전으로 대체는 가능하지만 점진적 변경은 불가능
◦
키-값 저장소를 이용해 객체에 접근할 수 있는 ID를 가져올 수 있다.
◦
저장은 1회, 읽기는 여러번
2.1. 객체 업로드
업로드 워크플로우
생성된 메타데이터
PUT /bucket-to-share/script.txt HTTP/I.I
Host: foo.s3example.org
Date: Sun, 12 Sept 2021 17:51:00 GMT
Authorization: [권한 문자열]
Content-Type: text/plain
Content-Length: 4567
x-amz-meta—author: Alex
[객체 데이터 4567 바이트]
Plain Text
복사
객체 업로드 요청 전문
2.2. 객체 다운로드
다운로드 워크플로우
GET /bucket-to-share/script.txt HTTP/I.I
Host: foo.s3example.org
Date: Sun, 12 Sept 2021 18:30:01 GMT
Authorization: [권한 문자열]
Plain Text
복사
다운로드 요청 전문
3. 상세설계
3.1. 데이터 라우팅 서비스
•
배치 서비스를 호출하여 데이터를 저장할 최적의 데이터 노드를 판단
•
데이터 노드에서 데이터를 읽어 API 서비스에 반환
•
데이터 노드에 데이터 기록
3.2. 배치 서비스
•
어느 데이터 노드에 데이터를 결정하는 역할을 담당
•
배치는 내부적으로 가상 클러스터 지도를 유지하는데, 클러스터의 물리적 형상 정보가 보관된다.
•
이 위치 정보를 활용해 데이터 사본이 물리적으로 다른 위치에 놓이도록 한다.
•
팩서스(Paxos), 래프트(Raft) 같은 합의 프로토콜을 사용해 구축을 권한다.
3.3. 데이터 저장 흐름
•
큰 흐름은 기록 아래와 같다.
◦
요청 → UUID 할당 → 클러스터 확인 → 저장노드 결정 → 데이터/UUID 전송 → 데이터 저장 및 다중화 → 등록 UUID 반환
•
데이터 일관성을 높이면 응답성이 낮아진다 → 서로간의 trade off 관계다.
•
모든 노드에 저장이 완료된 후 알림을 응답을 줄것인가?
◦
1번은 모두 저장되어야 응답준다.
◦
2번은 부 데이터 1에만 저장되면 응답준다.
◦
3은 메인 노드에 저장되면 바로 응답준다.
•
2, 3은 결과적 일관성의 한 형태로 볼 수 있다.
3.4. 데이터 저장
•
가장 단순한 저장방법 ⇒ 각각의 객체를 개별 파일로 저장
◦
작은 파일이 많아지면 성능이 떨어짐
◦
아이노드 용량 한계를 초과할 수 있음
•
작은 객체를 큰 파일로 모아서 해결한다.
◦
WAL과 같이 이미 존재하는 파일에 추가하는 방식
3.5. 객체 소재 확인
•
객체의 소재를 파악하는 데 필요한 정보
◦
객체가 보관된 데이터 파일
◦
데이터 파일 내 객체 오프셋
◦
객체의 크기
•
객체 소재를 찾는데는 적은 정보와 연산 속도를 요한다.
◦
고로 RDB가 가장 적합하다.
◦
파일형 데이터베이스인 SQLite 이용을 권한다.
3.6. 개선된 데이터 저장 흐름
3.7. 데이터 내구성
•
데이터 안정성은 아주아주 중요하다.
•
하드웨어 장애는 흔하게 발생하는데 이를 방지하는 방법은 독립적인 데이터 센터를 여러개 두어서 서로 데이터를 복제해두는 것이다.
◦
최소 3중으로 다중화 해놓는다.
◦
물리적 위치를 공유하지 않으면 극단전 상황에서도 가용성을 보장할 수 있다.
•
또 다른 방안으로는 소거코드를 이용하면 된다.
◦
같은 크기의 단위로 분할하여 각 데이터를 수학 공식을 사용해 패리티 블록을 만든다.
◦
이후 블록 소실 시 패리티 블록을 통해 데이터를 복구한다.
◦
데이터 다중화 시 소거 코드 사용시 최대 8개의 노드에서 데이터를 가져와야 한다.
▪
이는 소거 코드의 구조적 단점이다.
•
대규모 시스템은 메모리가 망가지는 경우도 자주 일어나는데 이때 체크섬을 통해서 해결할 수 있다.
◦
체크섬 알고리즘을 통해서 작은 데이터 블록을 만든다.
◦
이후 새로 계산한 체크섬과 달라지면 데이터가 망가진걸로 판단한다.
◦
계산이 같다고 온전할 확률이 100%는 아니지만 아주 낮은 확률이기에 현실적으로는 대다수 같다.
◦
체크섬 알고리즘은 MD5, SHA1, HMAC 등 다양하다.
•
각 객체 끝에 체크썸을 둔다.
3.8. 객체 버전
•
업로드 시 버저닝을 지원하며 기존 버전이 있을 경우 TIMEUUID값이 가장 큰 것이 최신버전이다.
◦
삭제 마킹을 해두면 종전 버전이 현재 버전이 된다.
3.9. 멀티파트 업로드
•
객체를 작은 단위로 쪼개서 독립적으로 업로드 한다.
•
이후 업로드ID와 태그를 따라 재조립하는 과정을 멀티파트라 부른다.
3.10. 쓰레기 수집
•
앞서 멀티파트 업로드 후 더 이상 쓰이지 않는 지원을 회수하는 과정
•
다음의 경우 쓰레기가 생길 수 있음
◦
객체의 지연된 삭제(lazy object deletion): 삭제했다고 표시는 하지만 실제로 지우지는 않는다.
◦
갈 곳 없는 데이터(orphaned data): 반쯤 업로드된 데이터, 또는 취소된 멀티파트 업로드 데이터.
◦
훼손된 데이터(corrupted data): 체크섬 검사에 실패한 데이터.