필자는 클라우드 클럽 7기 시즌1 스터디에서 시스템 붕괴 방지 위원회(줄여서 시붕방) 스터디를 참여하게 되었습니다.
3월 안으로 (가상 면접 사례로 배우는) 대규모 시스템 설계 기초 책을 읽고 국내 대기업의 시스템은 어떻게 이루어져 있을지 설계하고,
4월에는 해당 책의 2탄을 읽고 마찬가지로 설계 해보는 스터디입니다.
우리 팀의 대부분은 직장인이지만, 필자는 대학생이기에 읽어보니 새로운 것들이 너무나 많은 것 같아 블로그에 정리하는 것도 목표로 두고 있습니다.
단순히 책 내용을 요약정리하는 것이 아닌, 인상 깊었거나 궁금했던 점들 위주로 정리하는 것이 글의 주 내용입니다.
당연히 슬프게도 틀린 내용이 있을 수 있으니 질문은 언제나 환영입니다.
# 10장 알림 시스템 설계
날라갔네요 허허
분산 시스템에서 중복전송을 100% 막을 수는 없다고 하네요.
# 11장 뉴스 피드 시스템 설계
# 190p 두 종류의 팬아웃 모델1. 쓰기 시점에 팬아웃하는 모델(푸시모델)새로운 포스팅이 올라오자마자 뉴스 피드를 갱신시킴(해당 사용자의 캐시에 해당 포스팅을 기록장점: 뉴스 피드가 실시간으로 갱신, 친구 목록에 있는 사용자에게 즉시 전송, 새 포스팅이 기록되는 순간에 피드가 이미 갱신되므로 뉴스 피드를 읽는 데 드는 시간이 짧아진다.단점: 친구가 많은 사용자의 경우 친구 목록을 가져오고 그 목록에 있는 사용자 모두의 뉴스 피드를 갱신하는데 많은 시간이 걸린다.(핫키)서비스를 자주 이용하지 않는 사용자의 피드까지 갱신해야함으로 컴퓨팅 자원의 낭비로 보이기도 한다.
2. 읽기 시점에 팬아웃하는 모델(풀모델)요청 기반(온디맨드) 모델이다.장점: 비활성화된 사용자, 또는 서비스에 거의 사용하지 않는 사용자의 경우 이 모델이 유리,데이터를 친구 각각에게 푸시하는 작업이 필요없으므로 핫키 문제가 생기지 않는다.
단점: 각 사용자가 뉴스 피드를 읽는 데 많은 시간이 소요될 수 있다.
이 두 모델을 적절히 혼합하여, 대부분의 사용자에게는 푸시 모델을 사용하고, 친구나 팔로워가 아주 많은 경우 풀모델을 적용해볼 수 있다.
또한, 안정 해시를 적용해 부하를 줄인다.
# 12장 채팅 시스템 설계
채팅기능을 구현하기 위해서는 HTTP, 폴링, 롱 폴링, 웹 소켓 정도의 프로토콜을 고려해볼 수 있다.
웹 소켓을 제외하고는 실시간 채팅을 구현하기에 대부분 비효율적인 구조이다.
기본 설계안에는 일반적으로 HTTP통신을 하는 로드밸런서와 별개로 채팅 서버와 접속 상태 서버는 웹 소켓 통신으로 분리되어 있다.
서비스 디스커버리: 클라이언트에 가장 적합한 채팅 서버를 추천하는 역할.
이때 사용되는 기준으로는 클라이언트의 위치, 서버의 용량 등이 있다. 아파치 주키퍼 등의 서비스가 있다.
사용 가능한 모든 채팅 서버를 서비스 디스커버리에 등록시켜 놓고, 클라이언트가 접속을 시도하면 사전에 정한 기준에 따라 최적의 채팅 서버를 골라준다.
로직을 간단하게 말하자면, 사용자가 로드밸런서를 통해 특정한 API서버에 로그인 후 서비스 디스커버리가 여러 채팅 서버 중 적절한 채팅 서버를 골라서 관련 값을 리턴해주면, 사용자가 이제 해당 채팅서버로 웹소켓 통신을 보낸다.
특정 유저 두 명이서 채팅을 한다고 했을때, 웹소켓을 열어 하나의 채팅 서버에서 대화하는 것이 아닌, 각 유저 별 다른 채팅서버에 연결되어 있어도 상관없고, 메시지 동기화 큐에 의해 서로 소통하게 된다.

같은 유저가 여러 단말을 이용 중이라면 cur_max_message_id 와 같은 변수를 통해 각 디바이스 별 마지막 메시지 id를 키-값 저장소에 보관된 메시지와 비교해본다.
그리고 그룹 채팅의 경우 각 사용자 별로 별개의 메시지 큐를 가지고 있다. 사용자마다 메시지 수신 상태가 다르기 때문이라고 한다.
그룹이 크지않다면 메시지를 수신자별로 복사해서 큐에 넣는 작업이 비용이 문제가 되지 않는다.
# 217p 접속상태 확인을 위한 박동 검사
정말 단기간 인터넷이 끊어지는 경우 매번 오프라인 상태로 취급하는 것은 사용자 경험 측면에서 좋지않다.
대신 클라이언트가 주기적인 박동 이벤트를 접속상태 서버로 보내고 이벤트를 받은 지 x초 이내 새로운 이벤트를 접속상태 서버가 수신 받지 못하면 오프라인 상태로 전환한다.
작은 규모의 단체 방이라면 publish-subscribe 모델을 통해 접속상태 변경을 다른 모든 유저에게 제공하면 된다.
규모가 커진다면 이런 이벤트 하나에 수 십만 건의 이벤트가 발생하기 때문에 유저가 수동으로 조회하도록 유도하는 것이 좋다.
# 13장 검색어 자동완성 시스템
입력한 단어에 따른 검색어 추천을 위해 흔히 생각해볼 수 있는 방법은 검색된 단어마다 빈도를 +1 해주는 빈도 테이블(query, freqency)을 만들고, 아래 쿼리처럼 이용하는 방법이 가장 무난할 것이다.
select * from freqency_table where query like 'prefix%' order by freqency desc limit 5
하지만 위 방법은 데이터가 많아진다면 병목이 발생할 수 있다.
p231 최적화하기
데이터가 많아지더라도 병목현상이 생기는 것을 방지하기 위해,
1. trie(접두어 트리, retrieval에서 나옴) 자료구조를 사용한다.

접두어의 길이 p, 트라이 안에 있는 노드 개수 n, 주어진 노드의 자식 노드 개수 c라 가정하면,
시간복잡도는 O(p)+O(c)+O(c log c) 로 산출할 수 있다.
2. 접두어 최대 길이 제한
O(p)에서 O(상수) 처럼 제한을 걸어 시간을 단축할 수 있다.
3. 노드에 인기 검색어 캐시
미리 각 접두어 마다 인기 있는 검색어 5개씩을 캐싱하는 방법으로도 최적화를 이룰 수 있다.
p235 로그 취합 서버
앞선 방식들을 이용해도 트라이에 대해 개선할 부분은 여전히 있다.
매일 수천건의 질의에 따라 발생하는 빈도값 변화를 매번 갱신할 필요는 없다. 일주일에 한 번 정도만 또는 상황에 따라 하루에 한 번 정도만 갱신해도 트위터 같은 실시간 서비스가 아니라면 문제가 없을 것이다.
트라이 갱신방법은 크게 두가지로 볼 수 있을 것이다. 첫번째로는 갱신 때 마다 새로운 트라이를 만들어 기존 것을 대체하는 방법
두번째로는 각 노드를 개별적으로 갱신하는 방법. 당연히 각 노드를 개별적으로 갱신하는게 더 빠르지 않을까 싶었는데, 이는 하위 노드를 하나만 갱신하더라도, 이미 각 상위 노드마다 캐싱된 값들도 변경해줘야하기 때문에, 새롭게 트라이를 만들어 대체하는 것이 더 효율적이라고 한다.
# 14장 유튜브 설계
꼭 모든 서비스를 밑바닥에서부터 쌓아올릴 필요는 없다고 한다. 적절한 기술을 골라 주어진 시간 안에 설계를 마치는 것이 더 중요하다.
글로벌한 비디오 업로드/다운로드 서비스를 만들려면 CDN을 도입해야할텐데, 이 비용이 만만치 않다. 이를 줄이기 위해 지역별 인기 비디오, 조회수가 높은 비디오 등 특정 조건을 설정하여, CDN에 저장한다.
# 15장 구글 드라이브 설계
데이터를 업로드할 때, 사용자가 api서버와 통신하여 업로드 관련 내용을 처리한 후 디바이스 -> 블록 저장소 -> 클라우드 저장소 순으로 데이터가 업로드 된다.
이때, 디바이스에서 바로 클라우드 저장소로 가지 않고 블록 저장소를 거치는 이유는 파일을 블록으로 나누고, 압축 알고리즘을 적용하고 암호화 하는 등 업로드에 관한 처리를 담당해줘야하기 때문이다.
이때, 데이터 발송 최적화는 델타 동기화와 압축 두가지를 이용할 수 있는데, 각각 파일이 수정되면 전체 파일 대신 수정이 일어난 블록만 동기화, 블록 단위로 압축해 용량을 줄이는 방법이다.