본문 바로가기

server/system design

5. dropbox // google drive

1. 기능 요구 사항

  1. File Upload, Update, Delete and Download
  2. File search
  3. File and folder sync
  4. File history (versioning)

예외상황들

  1. File size limit
  2. 파일 권한 (수정 삭제)
  3. 동일 파일 수정에 따른 에러 핸들러

 

2. 추정 및 제약 사항

  • 총 사용자 : ~ 10억
  • 일일 활성 사용자 ~ 5천만
  • QPS : 하루 최대 5억 건 요청 (초당 6000건)
  • 저장 용량 추정평균 파일 크기는 1MB이라 할 때
  • 1MB * 100개 * 10억 = 10000PB
  • 모든 사용자가 평균 100개의 파일을 가지고 있다고 가정
  • 읽기 / 쓰기 비율 : 1 : 1
  • 예상 트래픽 : 초당 6G 파일 쓰기
  • 초당 6000건 * 1MB = 6GB
  • 메모리 사용량 : 각 사용자가 매일 5 개의 파일에 액세스하고 1MB 중 200KB 청크를 읽는다고 가정합니다. 따라서 80-20 규칙 (80 % 트래픽이 파일의 20 %에 대해 발생함)에 따라
  • 캐시 크기 = ((50 M x 200KB x 5) x 20) / 100 = 10 테라 바이트

 

3. 시스템 디자인

두 가지 시나리오를 지원해야 한다.

  • 파일 업로드 / 편집 / 삭제
  • 파일 보기 / 검색

파일을 저장할 오브젝트 스토리지 서버 (클라우드 스토리지), 클라이언트가 상호 작용할 블록 서버, 파일에 대한 메타 데이터 정보 (파일 이름, 파일 크기, 경로 등)를 저장할 데이터베이스 서버, 동기화 서버가 필요하다.

 

4. 시스템 API (REST API)

  1. upload(string uploadToken, fileInfo file, userInfo user)
  2. edit(string authToken, fileInfo file, userInfo user)
  3. delete(string authToken, fileInfo file, userInfo user)
  4. download(string authToken, fileInfo file, userInfo user)

 

 

5. Component Design

사용자

사용자 기기의 클라이언트 애플리케이션은 백엔드 클라우드 스토리지에 파일을 업로드, 다운로드 및 수정하기 위해 서비스와 통신한다.

  • 파일 업로드 / 다운로드

전체 크기로 파일을 업로드 / 다운로드하면 스토리지와 대역폭이 필요하며, 프로세스의 지연 시간도 늘어납니다. 사용자가 20MB 파일의 모든 데이터를 업로드한다면 다음과 같은 작업이 필요하다

  1. 20MB의 서버 메모리
  2. 20MB 파일을 업로드하기 위해 전체 시간 동안 묶여있는 요청 핸들러
  3. 높은 CPU 사용량
  4. 90% 후에 업로드가 실패하면 전체 파일을 다시 업로드해야 한다고 가정한다.
  5. 청크가 아닌 전체 파일을 가지므로 작은 편집을 위해 파일을 업데이트하는 효율적인 방법이 없다.

하나의 파일로 관리한다면 확장성을 매우 어렵게 만든다. 텍스트 하나만 수정해도 동기화가 일어나서 파일을 업로드하는데, 동시성이 아니므로, 하나의 파일을 업로드하는데 많은 시간이 걸린다.

전체 파일 업로드 방식을 사용하는 것은 현명한 선택이 아니다.

 

  • 효율적인 파일 전송 처리

업로드하기 쉽도록 파일을 작은 청크로 분할이 가능하다. 청크의 세부 사항은 메타 데이터에 포함될 수 있다. 청크 이름 지정은 청크 콘텐츠의 해시 값으로 수행할 수 있다.

 

청크 분할 전략은 여러 면에서 도움이 된다.

 

1. 대역폭 최적화

더 작은 청크 크기는 네트워크 대역폭 활용을 최적화한다.  업로드하는 동안 파일을 청크로 분할 한 다음 업로드할 수 있다. 파일 업로드가 실패한 경우 전체 파일을 다시 업로드 할 필요가 없으며 실패한 청크만 다시 시도된다.  사용자가 파일을 업데이트하면 수정된 청크 만 전송된다.

기존 20MB을 하나를 업로드하는 대신, 일정 청크로 나누고(10개), 수정된 5번째 청크를 업로드한다. 그러면 2MB의 청크 파일이 업로드된다.

클라이언트와 서버 간의 데이터 전송량이 적으면 네트워크 대역폭이 줄어들어 응답 시간을 줄일 수 있다.

 

2. 클라우드 스토리지 활용

전체 파일이 아닌 업데이트 시 서버에만 수정된 청크를 전송하므로 클라우드 스토리지 사용량이 감소한다.

 

3. 지연 시간 또는 동시성 활용

전체 파일을 한 번에 전송하면 작은 청크에 비해 훨씬 많은 시간이 소요된다. 여러 개의 작은 데이터 청크를 사용하면 동시성을 사용하여 다중 스레딩 또는 다중 프로세스를 사용하여 파일을 업로드 / 다운로드할 수도 있다.

 

4. 더 빠른 조회 및 버전 관리

업데이트 시 수정된 청크 만 전송하므로 파일 버전 기록을 입증하는 데 도움이 된다. 수정 된 청크를 직접 조회하여 수정 사항을 볼 수 있다.

 

 

청크 크기를 계산하는 방법

아래 매개 변수를 기반으로 최적의 청크 크기를 계산할 수 있다.

  1. 클라우드 스토리지 장치에서 초당 입력 / 출력 작업
  2. 네트워크 대역폭
  3. 저장소의 평균 파일 크기
  4. 메타 데이터 정보


※ 동기화 서버 구현 방법

한 클라이언트의 모든 업데이트가 다른 장치와 동기화되도록 하는 동기화 서버가 있다. 그러나 다른 클라이언트에서 발생하는 변경 사항을 수신하는 클라이언트를 어떻게 효율적으로 구현할 수 있을까?

 

기본 솔루션

첫 번째 기본 솔루션은 주기적으로 서버를 누르고 새로운 변경 사항을 확인하는 것이다. 그러나 이러한 접근 방식은 대부분의 경우 빈 응답을 받기 때문에 매우 비효율적이다. 네트워크 대역폭 낭비이며 서버에 추가 부하를 가할 필요가 없습니다.

 

HTTP long polling

효과적인 해결책은 HTTP long polling을 사용하는 것이다. long polling을 사용하면 클라이언트는 서버로부터 즉각적인 응답을 기대하지 않는다. 서버는 요청을 열린 상태로 유지하고 새로운 변경 사항을 기다린다. 클라이언트에 의해 새로운 수정 사항이 있을 때마다 서버는 즉시 HTTP 응답을 클라이언트에 보냅니다.

(하지만 long polling 또한 서버의 부담을 줄 수 있다.. websocket이나 SSE를 고려해야 할 것이다.)

참고 - 데스크톱 또는 웹 클라이언트와 달리 모바일 클라이언트는 일반적으로 사용자의 대역폭과 공간을 절약하기 위해 요청 시 동기화됩니다.

 

 

6. 파일 메타 데이터 데이터베이스

모든 청크의 메타 데이터와 인덱스를 추적해야 하므로 데이터베이스에 저장한다. 여기서 중요한 것은 실제 파일 / 청크 자체를 여기에 저장하지 않고 나중에 파일을 검색하기 위해 메타 데이터 정보만 저장한다. 또한 파일 기록 (버전 관리)을 유지 관리한다.

데이터베이스는 MySQL과 같은 관계형 데이터베이스이거나 cassandra, dynamoDB 또는 MongoDB와 같은 nosql 데이터베이스 일 수 있다.

 

  • 관계형 대 Nosql 데이터베이스
  1. 여러 사용자가 동시에 동일한 파일에 있을 수 있으므로 데이터 일관성을 보장해야 한다. NoSQL 데이터 저장소는 성능과 확장 성을 위해 ACID 속성을 지원하지 않기 때문에  동기화 서비스에서 프로그램적으로 ACID 속성을 구현해야 한다.  관계형 데이터베이스 (MySQL)는 ACID 속성을 지원하므로 장점이 있다.
  2. 관계형 데이터베이스는 MySQL처럼 확장하기 어렵고, 샤딩 또는 마스터 슬레이브 기술을 사용해야 하며 새로운 업데이트를 위해 이러한 여러 데이터베이스가 더 어려워진다.

이 문제를 극복하기 위해 데이터베이스와 직접 통신하는 대신 캐시 서버를 추가한다.

 

저장할 메타 데이터

메타 데이터 데이터베이스는 파일 / 청크, 사용자 및 작업 영역에 대한 버전 관리 및 메타 데이터 정보를 유지 관리한다 메타 데이터 데이터베이스는 데이터 일관성을 충족하는 MySQL과 같은 관계형 데이터베이스 또는 NoSQL 데이터베이스 일 수 있다.

다음은 샘플 메타 데이터 예제이다.

 

동기화 서비스

모든 새로운 업데이트에 대해 동기화 서비스는 업데이트를 효율적으로 처리하고 다른 구독 장치에 변경 사항을 적용하여 로컬 DB와 원격 DB를 동기화 상태로 유지해야 한다.

동기화 서비스는 불필요한 네트워크 대역폭을 피하고 더 나은 응답 시간을 달성하기 위해 가능한 한 적은 데이터를 전송하도록 설계되어야 한다. 서버와 클라이언트는 해시(SHA-256)를 계산하여 청크 업데이트 여부를 확인할 수 있다. 서버에서도 비슷한 해시를 가진 청크 (다른 사용자의 청크 포함)가 있는 경우 데이터 중복 제거를 피하기 위해 새 복사본을 만드는 대신 동일한 청크를 사용할 수 있다.

파일 업로드 시 파일을 쪼개어 multiple chunks 업데이트되는 청크만 저장하여 여러 버전의 파일을 업로드, 저장 및 유지하기가 더 쉽다.

이러한 청크는 청크 콘텐츠 자체의 해시로 명명할 수 있다.

또한 다운로드 / 동기화할 때 청크를 사용하여 파일을 다시 생성하기 위해 모든 청크 이름과 순서 (메타 데이터) 정보를 저장해야 한다.

 

 

7. 클라우드 / 블록 스토리지

클라우드 / 블록 스토리지는 사용자가 서비스에 업로드 한 모든 청크를 저장한다. 스토리지와 별도로 메타 데이터 데이터베이스가 있으므로 Amazon S3, Azure 등 모든 클라우드 스토리지 접근 방식을 사용할 수 있다.

Dropbox가 초기엔 aws S3를 사용했다

 

데스크톱 / 모바일에 파일 업로드 클라이언트가 설치되어 있다고 가정해 보자.

Client는 동기화 폴더로 식별된 폴더를 모니터링하고 원격 클라우드 스토리지와 동기화한다. Client는 동기화 서비스와 상호 작용하여 파일 메타 데이터 업데이트 (예 : 파일 이름, 크기, 수정 날짜 등)를 처리한다. 또한 실제 파일을 저장하기 위해 백엔드 Cloud Storage와 상호 작용한다.

Desktop Client의 가장 중요한 요구 사항 중 일부에는 파일 업로드 및 다운로드, 동기화 폴더의 파일 변경 감지, 오프라인 또는 동시 업데이트로 인한 충돌 처리가 포함되어야 한다. 데스크톱 클라이언트의 주요 구성 요소는 아래와 같이 Watcher, Chunker, Indexer 및 내부 DB이다.

  • Watcher는 동기화 폴더를 모니터링하고 사용자가 파일 또는 폴더를 생성, 삭제 또는 업데이트할 때 수행 한 모든 작업을 인덱서에 알린다
  • 청커는 파일을 청크라고하는 작은 조각으로 분할한다. 파일을 재구성하려면 청크가 올바른 순서로 다시 결합된다. 청킹 알고리즘은 사용자가 수정 한 파일 부분을 감지하고 해당 부분만 Cloud Storage로 전송하여 클라우드 스토리지 공간, 대역폭 사용량 및 동기화 시간을 절약할 수 있다
  • 인덱서는 감시자로부터 받은 이벤트를 처리하고 수정된 파일의 청크에 대한 정보로 내부 데이터베이스를 업데이트한다. 청크가 Cloud Storage에 성공적으로 제출되면 인덱서는 메시지 큐 서비스를 사용하여 동기화 서비스와 통신하여 변경 사항으로 메타 데이터 데이터베이스를 업데이트한다.
  • 내부 데이터베이스는 청크, 파일, 버전 및 파일 시스템의 위치를 기록한다.

 

8. 데이터베이스 스키마

  • 사용자는 유료 서비스에 가입할 수 있다.
  • 각 구독에는 하나의 계획이 있어야 한다.
  • 각 사용에는 최소한 하나의 장치가 있어야 한다.
  • 각 장치에는 최소한 하나의 개체 (파일 또는 폴더)가 있다. 사용자가 등록하면 최소한 하나의 객체를 가지고 있는지 확인하기 위해 루트 폴더를 만든다.
  • 각 개체에는 청크가 있을 수 있다. 파일만 청크를 가질 수 있고 폴더는 청크를 가질 수 없다.
  • 각 개체는 한 명 또는 여러 명의 사용자와 공유할 수 있다. 이 매핑은 AccessControlList에서 유지된다.

 

 

9. 메시지 큐 서비스

 

 

아키텍처의 중요한 부분은 상당한 양의 읽기 및 쓰기를 처리할 수 있어야 하는 메시징 미들웨어이다.

클라이언트와 동기화 서비스 인스턴스 간의 비동기 메시지 기반 통신을 지원하는 확장 가능한 메시지 큐 서비스를 사용해야 한다.

메시지 큐 서비스는 시스템의 분산된 구성 요소 간의 비동기 및 메시지 기반 통신을 지원합니다. 메시지 큐 서비스는 확장성 / 가용성이 높고 안정적인 큐에 원하는 수의 메시지를 지속적으로 저장할 수 있어야 한다. 또한 메시지 큐 서비스는 동기화 서비스의 여러 인스턴스에 대한 부하 분산 및 탄력성을 제공한다.

위의 그림엔 메시지 큐 서비스에서 사용되는 두 가지 유형의 큐가 있다

  1. 요청 대기열 큐는 모든 클라이언트 간에 공유되는 전역 대기열입니다. 동기화 서비스를 통해 메타 데이터 데이터베이스를 업데이트하라는 클라이언트의 요청은 요청 대기열로 전송
  2. 클라이언트에 해당하는 response 큐는 각 클라이언트에 업데이트 메시지를 전달
  3. 클라이언트가 메시지를 수신하면 큐에서 메시지가 삭제
  4. 여러 클라이언트에게 보내야 하는 업데이트 메시지일 경우 공유할 수 있도록 각 클라이언트에 대해 별도의 request 큐를 생성 및 메시지 전송
  5. 4번의 request의 데이터는 다시 response 큐로 전송되어 클라이언트로 전송

요청 큐는 모든 클라이언트가 공유하는 글로벌 큐이다. 각 수정에 대해 요청은 먼저 글로벌 큐로 이동하여 메타 데이터 DB를 업데이트하고 그 후 동기화 서비스는 메타 데이터를 업데이트하고 업데이트 메시지는 응답 큐를 통해 가입된 모든 클라이언트로 전송된다

RabbitMQ, Apache Kafka 등은 높은 확장 성 및 고성능과 함께 동기화 서비스의 여러 인스턴스에 대한 로드 밸런싱 및 탄력성을 제공해야 한다.

 

 

10. 클라우드 스토리지

Cloud Storage / Block 서버는 사용자가 업로드 한 파일 청크를 저장한다. 클라이언트는 Cloud Storage와 직접 상호 작용하여 클라우드 제공자가 제공하는 API를 사용하여 객체를 전송한다. 클라우드 스토리지를 구축하고 유지하고 싶지 않다면 Amazon s3 like service를 사용할 수 있다.

 

 

 

참고

https://www.youtube.com/watch?v=8HaXU4aNTSg&ab_channel=CodingSimplified

https://systemdesignprimer.com/dropbox-system-design/

https://medium.com/@narengowda/system-design-dropbox-or-google-drive-8fd5da0ce55b

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

'server > system design' 카테고리의 다른 글

7. UBER // 쏘카 // lyft // 카카오택시  (0) 2021.08.09
6. tinder  (0) 2021.08.01
4. instagram // Flickr // Picasa  (0) 2021.07.23
3. twitter  (0) 2021.07.21
2. pastebin ( text storage site )  (0) 2021.07.17