7장 분산 시스템을 위한 유일 ID 생성기 설계

1단계 문제 이해 및 설계 범위 확정

  • ID 특성
    • 유일성, 정렬 가능
  • 새로운 레코드의 ID의 증가 값
    • 증가 값이 1씩 증가하지는 않지만, 과거의 값보다 나중에 만든 값이 더 큰 ID를 갖는다.
  • ID는 숫자로 구성
  • 시스템 규모

2단계 개략적 설계안 제시 및 동의 구하기

  • 분산 시스템에서 유일성이 보장되는 ID 생성 방법
    • 다중 마스터 복제(multi-master replication)
    • UUID(Universally Unique Identifier)
    • 티켓 서버(ticket server)
    • 트위터 스노플레이크(twitter snowflake) 접근법

  • 다중 마스터 복제

  • 데이터 베이스의 auto_increment 기능을 활용한다.
  • 증가 값은 k만큼 증가
  • k = 현재 사용 중인 데이터베이스 서버 수
  • 단점
    • 여러 데이터 센터에 걸쳐 규모를 확장하기는 어렵다.
    • ID 유일성은 보장되지만, 시간 흐름에 맞추어 커지도록 하기에는 힘들다.
    • 서버 추가나 삭제할 때에도 잘 동작하기 힘들다.

  • UUID

  • 컴퓨터 시스템에 저장되는 정보를 유일하게 식별하기 위한 128비트 수
  • 서버 간 조율 없이 독립적으로 생성 가능
  • 각 웹 서버는 별도 ID 생성기로 독립적인 ID 생성이 가능하다.
  • 장점
    • UUID 만드는 것이 간단하고, 동기화 이슈도 없다.
    • 규모 확장이 간단하다.
  • 단점
    • ID가 128비트이기 때문에 길다.
    • ID 시간순 정렬이 어렵다.
    • ID에 숫자가 아닌 값이 포함된다.

  • 티켓 서버

  • auto_increment 기능을 갖춘 데이터베이스 서버(티켓 서버)를 중앙 집중형으로 하나만 사용한다.
  • 장점
    • 유일성이 보장되는 숫자 ID를 간단하게 만들 수 있다.
    • 규모가 적은 애플리케이션에 적합
  • 단점
    • 티켓 서버가 SPOF(Single-Point-of-Failure)
    • 장애가 발생하면, 해당 서버를 사용하는 모든 시스템에 영향을 끼친다.

  • 트위터 스노플레이크 접근법

  • ID를 바로 생성하기 전에, divide and conquer 적용
  • sign : 음수, 양수 구분
  • timestamp : 기원 시각(epoch) 이후로 몇 밀리초가 경과했는지 나타내는 값
  • 데이터센터 ID
  • 서버 ID
  • 일련번호 : 각 서버에서 ID 생성할 때마다 일련 번호를 1씩 증가

📌 대규모 시스템 설계 기초

13장 : 검색어 자동 완성 시스템

  • 검색어를 입력하고 있을 때, 자동으로 완성되는 검색어 만들기
  • 검색어 자동완성(autocomplete, typeahead, search-as-you-type, incremental search)
  • 예제) 검색창에 단어를 입력했을 때 자동완성 되는 검색어들


1단계 문제 이해 및 설계 범위 확정

  • 요구사항 파악하기
    • 자동완성 검색어는 첫 부분인지, 중간 부분인지
    • 몇 개의 검색어를 표시
    • 검색어를 고르는 기준 : 인기순, 최신순
    • 맞춤법 검사 기능을 제공
    • 다국어 지원
    • 대문자, 소문자, 특수 문자 처리 여부
    • 사용자의 수는?
  • 요구사항 정리하기
    • 빠른 응답 속도
    • 검색어의 연관성
    • 정렬 : 계산 결과는 인기도 등의 순위 모델을 사용해야 한다.
    • 규모 확장성 : 트래픽을 감당할 수 있도록 확장 가능해야 한다.
    • 고가용성 : 일부에 장애가 발생하더도, 시스템은 계속 실행되어야 한다.
  • 개략적 규모 추정
    • 일간 사용자 천 만명 가정
    • 한 사용자는 평균적으로 일일 10건 검색
    • 평균적으로 20바이트의 데이터를 입력한다고 가정
      • 문자 인코딩 방법은 ASCII를 사용하면 1문자 = 1바이트
      • 평균적으로 질의문은 4개의 단어로 이루어져 있다.
      • 각 단어는 평균적으로 다섯 글자로 구성
      • 결과 = 질의당 평균 4 X 5 = 20 바이트
    • 검색 창에 글자를 입력하면, 클라이언트는 백엔드에 요청을 보낸다.
    • 평균적으로 1회 검색당 20건의 요청이 백엔드에 전달
    • 계산 = 1000만 사용자 X 10 질의 / 일 X 20자 / 24시간 / 3600초)
    • 초당 24000건의 QPS
    • 질의 중에 20%가 신규 검색어라면, 1000만 사용자 X 10질의 / 일 X 20자 X 20%
    • 0.4GB의 신규 데이터가 시스템에 추가된다.

2단계 개략적 설계안 제시 및 동의 구하기

  • 시스템 구성 정리
  1. 데이터 수집 서비스(data gathering service)
  2. 질의 서비스(query service)

데이터 수집 서비스

  • 사용자가 입력한 질의를 실시간으로 수집하는 서비스
  • 데이터가 많은 애플리케이션에서는 어려운 작업
  • 질의문과 사용빈도를 저장하는 빈도 테이블(frequency table)이 있다고 가정

질의 서비스

  • 주어진 질의에 다섯 개의 인기 검색어를 정렬하는 서비스
  • 주어진 빈도 테이블
    • query : 질의문을 저장하는 필드
    • frequency : 질의문이 사용된 빈도를 저장하는 필드

  • 사용자가 "tw" 를 검색하게 되면, 아래와 같이 자동 완성 검색표가 완성 되어야 한다.
검색어 : tw
twitter
twitch
twilight
twin peak
twitch peak

 

  • 질의문 SQL
SELECT * FROM frequency_table
WHERE query Like `prefix%`
ORDER BY frequency DESC
LIMIT 5

 

  • 하지만 위와 같이 설계한다면, 데이터가 많아질 때 데이터베이스의 병목 현상이 일어날 수 있다.

3단계 상세 설계

  • 트라이(trie) 자료구조
  • 데이터 수집 서비스
  • 질의 서비스
  • 규모 확장이 가능한 저장소
  • 트라이 연산

트라이 자료구조

  • 관게형 데이터베이스를 사용할 때 가장 인기 있는 다섯 개의 질의문을 골라내는 방법은 효율적 X
  • 트라이 자료구조로 해결
  • 트라이
    • 문자열들을 간략하게 저장할 수 있는 자료구조
    • 트리 형태의 자료구조
    • 루트 노드는 빈 문자열
    • 각 노드는 하나의 문자만 저장한다.
  • tree, try, true, toy, wish, win 질의어를 트라이에 보관할 때

  • 빈도를 따라 정렬해야 하기 때문에, 빈도 정보를 저장할 빈도 테이블

  • 빈도 테이블을 트라이 노드에 저장하게 되면

  • 질의어 찾는 방법
    • p(접두어 길이), n(트라이 노드 개수), c(주어진 노드의 자식 노드 개수)
      • 해당 접두어를 표현하는 노드를 찾기 -> O(p)
      • 해당 노드부터 시작하는 하위 트리를 탐색해서, 모든 유효 노드를 찾는다. -> O(c)
      • 유효 노드를 정렬해서 가장 인기 있는 k개를 찾는다. -> O(clogc)
  • 예시 : k = 2, 질의어 'be'
    • 접두어 노드 'be' 찾기
    • 해당 노드의 하위 트리들을 탐색해서 모든 유효 노드를 찾는다.
    • 유효 노드를 정렬해서 2개만 고른다.

  • 최악의 경우에는 전체 트라이를 검색해야 하는 문제 발생
    • 해결 방법
      • 접두어 최대 길이 제한 두기
      • 각 노드에 인기 검색어를 캐시


데이터 수집 서비스

  • 계속 대용량의 데이터를 트라이에 갱신하면 성능 저하
  • 인기 검색어는 자주 바뀌지 않을 것이기 때문에 자주 갱신할 필요가 없다.
  • ex. 트위터는 검색어를 자주 갱신해주어야 하지만, 구글 같은 경우 자주 바꿔줄 필요 X

  • 데이터 분석 서비스 로그 : 검색창에 입력된 질의에 대한 원본 데이터

  • 로그 취합 서버 : 제각각인 데이터 형식들을 잘 집계(aggregation) 해서 쉽게 소비할 수 있도록 한다.
    • 트위터 같이 결과를 빨리 보여줘야 하면 데이터 취합 주기를 짧게, 대부분의 경우 일주일에 한 번 정도 로그를 취합해도 된다.
  • 취합된 데이터 : time, frequency 필드를 통해 해당 쿼리에 대한 시작한 날짜, 해당 주에 사용된 횟수의 합을 저장한다.
  • 작업 서버 : 주기적으로 비동기적 작업을 실행하는 서버 집합
    • 트라이 자료구조를 만들고, 트라이 데이터베이스에 저장하는 역할
  • 트라이 캐시 : 분사 산 캐시 시스템으로, 트라이 데이터를 메모리에 유지하여 읽기 연산 성능을 높인다. 매주 갱신하도록 한다.
  • 트라이 데이터베이스 : 지속성 저장소. 문서 저장소 or 키 값 저장소 형태로 사용
    • 문서 저장소(document store)
      • 새 트라이를 매주 만들어야 해서, 주기적으로 트라이를 직렬화하여 DB에 저장
      • MongoDB 같은 문서 저장소 활용
    • 키 값 저장소
      • 해시 테이블 형태로 변환
        • 트라이에 보관된 모든 접두어를 해시 테이블 키로 변환
        • 각 트라이 노드에 보관된 모든 데이터를 해시 테이블 값으로 변환


질의 서비스

  • 비효율성 개선 설계
  1. 검색 질의가 로드 밸런서로 전송
  2. 로브 댈런서가 해당 질의를 API 서버에 전송
  3. API 서버는 트라이 캐시에서 데이터를 가져와 해당 요청에 대한 자동완성 검색어 제안 응답 구성
  4. 데이터가 트라이 캐시에 없는 경우의 데이터는 데이터베이스에서 가져와서 캐시에 채운다.


트라이 연산

  • 트라이 생성 : 작업 서버 담당. 데이터 분석 서비스의 로그나 데이터베이스로부터 취합된 데이터를 이용
  • 트라이 갱신
    • 매주 한 번 갱신해서 기존 트라이를 대신 하는 방법
    • 각 노드를 개별적으로 갱신하는 방법 -> 트라이가 적을 때만 좋다.
      • 트라이 노드를 갱신할 때 그 위의 상위 노드도 갱신해야 하기 때문

  • 검색어 삭제
    • 폭력적인 질의어는 자동완성에서 제거한다.
    • 트라이 캐시 앞에 필터 계층(Filter layer)을 두고 부적절한 질의어가 반환되지 않도록 한다.
    • 검색어를 물리적으로 삭제하는 것은 다음 업데이트에 비동기적으로 진행


저장소 규모 확장

  • 서비스 크기가 확장될 때 규모를 확장할 수 있도록 해결한다.
  • 첫 글자 기준으로 샤딩(sharding) 해보기
    • 영어는 26개의 알파벳으로 이루어져 있다.
    • 서버 2개인 경우, a부터 m까지 첫 번째 서버, 나머지 두 번째 서버
    • 서버 3개인 경우, a부터 i까지 첫 번째 서버, ...
  • 다른 방법으로는 샤딩을 계측적으로
    • 첫 번째 샤딩에 첫 번째 글자
    • 두 번째 샤딩에 두 번째 글자
  • 하지만 데이터가 균등하지 않게 분배되는 문제가 생길 수 있따.
  • ex. c로 시작하는 단어가 x로 시작하는 단어보다 많은 경우
  • 검색어 대응 샤드 관리자(shard map manager)로 어떤 검색어가 어느 저장소 서버에 저장되는지에 대한 정보를 관리한다.


4단계 마무리

  • 다국어 지원을 하려면?
    • 트라이에 유니코드(Unicode) 데이터를 저장해야 한다.
    • 유니코드 : 모든 문자 체계를 지원하는 표준 인코딩 시스템
  • 국가별로 인기 검색어 순위가 다르면?
    • 국가별로 다른 트라이를 사용
    • 트라이를 CDN에 저장해서 응답속도 개선
  • 실시간으로 변하는 검색어 추이를 반영하려면?
    • 스트림 프로세싱에 필요한 시스템
    • 하둡 맵리듀스, 스파크, 카프카 등이 필요하다.

안정 해시 사용 : 수평적 규모 확장성을 위해 요청, 데이터를 서버에 균등하게 나누기 

 

 

해시 키 재배치(refresh) 문제

  • N개의 서버가 있을 때, 부하를 균등하게 나누기 위해 해시 함수 사용
  • serverIndex = hash(key) % N(서버의 개수)
  • hash(key0) % 4 = 1인 경우, 클라이언트가 캐시에 보관된 데이터를 가져오기 위해 서버 1에 접속
  • 서버 풀(server pool) 크기가 고정되어 있고, 데이터 분포가 균등할 때 잘 동작한다.
  • 대규모 캐시 미스(cache miss) : 하지만 서버가 추가되거나 기존 서버가 삭제되면 문제가 생긴다. 
    • 1번 서버 장애 -> 1번 동작 중지 -> 서버 풀 크기 3 변경 -> 나머지 서버 인덱스 값들이 달라짐 

 

안정 해시(consistent has)

  • 해시 테이블 크기가 조정될 때 평균적으로 k/n개의 키만 재배치하는 해시 기술(k:키의 개수, n:슬롯 개수)

 

해시 공간과 해시 링

  • 해시 함수 f를 SHA-1을 사용한다고 가정할 경우
    • 함수의 출력 값 : x0, x1, x2, .. xn
    • 해시 공간 범위 : 0 ~ (2^160)-1
    • 해시 공간 표현

  • 해시 링(hash ring) : 해시 공간의 양쪽을 구부려 접은 형태

 

서버 조회

  • 해시 함수를 사용해서 서버 4개를 해시 링 위에 해시 서버 배치 : s0, s1, s2, s3
  • 해시 링 어느 지점에 해시 키 배치 : k0, k1, k2, k3
  • 키가 저장되는 서버 : 해당 키 위치로부터, 시계 방향으로 링을 탐색해 나가면서 만나는 첫 번째 서버

 

 

서버 추가

  • 키 가운데 일부만 재배치
  • 서버 4추가 시, key0만 재배치
  • key0은 서버 4에 저장된다.

 

서버 제거

  • 하나의 서버가 제거되면 키 가운데 일부만 재배치
  • 서버1이 삭제되면, key1만 서버 2로 재배치

 

기본 구현법 두가지 문제

  • 어떤 서버는 굉장히 큰 해시 공간을 할당 받는 상황이 생길 수 있다.
  • ex. s1 삭제 -> s2 파티션이 다른 파티션 대비 2배로 커짐
  • 가상 노드(virtual node) 또는 복제(replica) 사용 : 키의 균등 분포 문제 해결을 위해 사용한다.

 

가상 노드

  • 실제 노드 또는 서버를 가리키는 노드
  • 하나의 서버는 링 위에 여러 개 가상 노드를 가질 수 있다.
  • 키가 저장될 서버 : 키의 위치로부터 시계 방향으로 링을 탐색하다가 만나는 최초의 가상 노드
  • ex. k0은 가상 노드 s1_1가 나타내는 서버1에 키 저장

 

 

재배치할 키 결정

  • 서버 추가 or 제거되면 데이터 일부를 재배치해야 한다.

마무리 - 안정 해시 이점

  • 서버 추가 or 삭제시 재배치되는 키의 수 최소화
  • 데이터의 균등한 분포 가능 -> 수평적 규모 확장성 용이
  • 핫스팟 키 문제 축소 

처리율 제한 장치(rate limiter)

  • 클라이언트나 서비스가 보내는 트래픽의 처리율을 제어하기 위한 장치
  • API 요청 횟수가 제한 장치에 정의된 임계치(threshold)를 넘어가면 추가로 도달한 모든 호출은 처리가 중단된다.
    • 사용자는 초당 2회 이상 새 글을 올릴 수 없다.
    • 같은 IP 주소로 하루에 10개 이상 계정 생성 불가능
    • 같은 디바이스로 주당 5회 이상 리워드 요청 불가능

 

API 처리율 제한 장치의 장점

  • Dos(Denial of Service) 공격에 대한 자원 고갈을 방지 
    • ex. 트위터 : 3시간 동안 300개 트윗만 게시 가능
    • ex. 구글 독스 API : 사용자당 분당 300회의 read 요청까지 허용
    • 처리율 제한 장치는 추가 요청에 대해 처리를 중단함으로써 Dos 공격을 방지한다.
  • 비용 절감 : 추가 요청에 대한 처리 제한은 서버를 많이 두지 않아도 되고, 우선순위가 높은 API에 더 많은 자원 할당 가능
  • 서버 과부하 방지 : 봇에서 오는 트래픽이나 사용자의 잘못된 이용을 처리율 제한 장치로 걸러낼 수 있다.

 

 

1단계 : 문제 이해 및 설계 범위 확정

  • 처리율 제한 장치 구현에 여러 가지 알고리즘 적용 가능

 

  • 클라이언트 측 제한 장치? 서버 측 제한 장치인지 확인
  • 서버측 API 인 경우, 어떤 기준으로 API 호출을 제어할 것인지(IP 주소 or 사용자 ID)
  • 시스템 규모는? 사용자가 얼마나 많은 제품인지?
  • 분산 환경에서 동작해야 하는 시스템인지? -> 하나의 처리율 제한 장치를 여러 서버나 프로세스에 공유할 수 있어야 한다.
  • 처리율 제한 장치는 독립된 서비스인가? 애플리케이션 코드에 포함되는지? -> 제한 장치에 장애가 생겨도 전체 시스템에 영향 X
  • 사용자에게 처리율 제한 장치에 걸러진 경우 알림을 줄 것인지? -> 예외 처리

2단계 : 개략적 설계안 제시 및 동의 구하기

  • 처리율 제한 장치는 어디에 둘 것인가?
    • 클라이언트 측
      • 일반적으로 클라이언트 처리율 제한은 안정적인 장소X
      • 클라이언트 요청은 쉽게 위변조가 가능하다.
    • 서버 측 

1. 처리율 제한 장치를 API 서버에 두는 방식  

 

2. 처리율 제한 미들웨어로 API 서버에 가는 요청을 통제하는 방식

    •  

 

  • 예제로, API 서버의 처리율이 초당 2개 요청으로 제한되었을 경우
  • 클라이언트가 3번째 요청을 앞의 2개의 요청들과 같은 초 범위 내에 전송 되었을 때
  • 세 번째 요청은 처리율 제한 미들웨어로 인해 클라이언트에게 HTTP status Code(429 : Too many requests)를 반환한다.

 

MSA(Microservice Architecture)

  • 마이크로서비스 아키텍처
  • 작고, 독립적으로 배포 가능한 각각의 기능을 수행하는 서비스로 구성된 프레임워크
  • 처리율 제한 장치는 보통 API gateway(게이트웨이)라 불리는 컴포넌트에 구현된다.
  • API gateway 
    • 처리율 제한, SSL 종단(termination), 사용자 인증, IP 허용 목록 관리 등을 지원하는 완전 위탁관리형 서비스
    • 클라우드 업체가 유지 보수를 담당하는 서비스
    • 처리율 제한을 지원하는 미들웨어로 외우기

 

처리율 제한 장치 배치 기준

  • 프로그래밍 언어, 캐시 서비스 등 사용 기술 스택 점검하기 
  • 사업에 맞는 처리율 제한 알고리즘 찾기
    • 서버 측에서 모두 구현하면 알고리즘의 선택이 자유롭지만
    • 제 3사업자가 제공하는 게이트웨이를 사용하면 선택지 제한이 생길 수 있다.
  • MSA 구조이고, 사용자 인증과 IP 허용목록 관리 등을 API 게이트웨이로 포함하고 있으면, 처리율 제한 기능도 게이트웨이에 포함해야할 수 있다.

 

처리율 제한 알고리즘

  • 토큰 버킷(token bucket)
  • 누출 버킷(leaky bucket)
  • 고정 윈도 카운터(fixed window counter)
  • 이동 윈도 로그(sliding window log)
  • 이동 윈도 카운터

 

토큰 버킷 알고리즘

  • 보편적인 처리율 제한 알고리즘
  • 토큰 버킷 : 지정된 용량을 갖는 컨테이너.
  • 사전 설정된 양의 토큰이 주기적으로 채워진다. 버킷이 꽉 차면 토큰 추가 불가능
  • 용량이 4인 버킷에서 토큰 공급기가 매초 2개씩 토큰을 추가할 때, 오버플로(overflow) 된 토큰은 버려진다.

  • 충분한 토큰이 있는 경우, 버킷에서 토큰을 하나 꺼내고 요청을 시스템에 전달
  • 충분한 토큰이 없는 경우, 해당 요청은 버려진다.

  • 버킷 알고리즘은 2가지 인자(parameter)를 받는다.
    • 버킷 크기 : 버킷에 담을 수 있는 토큰의 최대 개수
    • 토큰 공급률(refill rate) : 초당 몇 개의 토큰이 버킷에 공급되는가
  • 버킷은 몇개 사용해야 하는지?
    • API 엔드포인트(endpoint)마다 별도의 버킷을 둔다.
    • 사용자가 하루에 한 번 포스팅, 친구는 150명까지 추가 가능, 좋아요 버튼은 다섯 번 까지 누를 수 있으면 사용자는 3개의 버킷을 두어야 한다.
    • IP 주소별로 처리율을 제한하면 -> IP 주소 개수마다 버킷 할당
    • 시스템 처리율을 초당 10,000개 요청으로 제한하면 -> 모든 요청이 하나의 버킷을 공유
  • 토큰 버킷 알고리즘 장점
    • 쉬운 구현과 효율적인 메모리 사용
    • 짧은 시간에 집중되는 트래픽 처리 가능
  • 토큰 버킷 알고리즘 단점
    • 2가지 인자(버킷 크기, 토큰 공급률)의 값을 적절하게 튜닝하는 것이 까다롭다.

 

누출 버킷 알고리즘(leaky bucket)

  • 토큰 버킷 알고리즘과 유사하지만, 요청 처리율이 고정되어 있다.
  • FIFO(First-In-First-Out) 큐로 구현
  • 동작 원리
    • 요청 도착
    • 큐의 크기 확인
      • 빈자리가 있으면 큐에 요청 추가
      • 가득찬 경우 새 요청 버리기
    • 지정된 시간마다 큐에서 요청 꺼내어 처리

  • 누출 버킷 알고리즘은 2가지 인자(parameter)를 받는다.
    • 버킷 크기 : 큐 사이즈와 같은 값
    • 처리율(outflow rate) : 지정된 시간당 몇 개의 항목을 처리할지 지정하는 값
  • 누출 버킷 알고리즘 장점
    • 제한된 큐의 크기 : 효율적인 메모리 사용량
    • 고정된 처리율 : 안정적 출력이 필요한 경우에 적합 
  • 누출 버킷 알고리즘 단점
    • 단시간에 많은 트래픽이 쌓이는 경우, 큐에 오래된 요청이 쌓이고, 요청 처리를 못하면 최신 요청들이 버려진다.
    • 두 개의 인자의 올바른 튜닝의 까다로움

 

고정 윈도 카운터 알고리즘(fixed window counter)

  • 타임라인(timeline)을 고정된 간격의 윈도(window)로 나눈다. 각 윈도에 카운터(counter)를 붙인다.
  • 요청이 접수되면 counter 1씩 증가
  • 카운터 값이 임계치(threshold)에 도달하면, 새로운 요청은 새 윈도가 열릴 때까지 버려진다.
  • 큰 문제점 : 윈도 경계 부근에 순간적으로 많은 트래픽이 집중되면, 윈도 할당량보다 더 많은 요청이 처리될 수 있다.

 

  • 고정 윈도 카운터 알고리즘 장점
    • 메모리 효율이 좋고, 이해하기 쉽다.
    • 특정 트래픽 패턴을 처리하기 적합하다. (윈도가 닫히는 시점에 카운터 초기화)
  • 고정 윈도 카운터 알고리즘 단점
    • 윈도 경계 부근에 순간적으로 많은 트래픽이 몰리면, 시스템 처리 한도보다 많은 양의 요청을 처리한다.

 

이동 윈도 로깅 알고리즘

  • 고정 윈도 카운터 알고리즘 문제 해결
  • 요청의 타임스탬프(timestamp)를 추적한다. 타임스탬프 데이터는 Redis의 정렬 집합(sorted set) 같은 캐시에 보관
  • 새 요청이 오면 만료된 타임 스탬프를 제거한다. (만료된 타임스탬프 = 그 값이 현재 윈도의 시작 시점보다 오래된 타임스탬프)
  • 새 요청 타임스탬프를 로그(log)에 추가
  • 로그의 크기가 허용치보다 같거나 작으면 요청을 시스템에 전달
  • 로그의 크기가 허용치보다 크면 처리 거부

  • 이동 윈도 로깅 알고리즘 장점
    • 처리율 제한 메커니즘이 정교하기 때문에 허용되는 요청 개수가 시스템 처리율 한도를 넘지 않는다.
  • 이동 윈도 로깅 알고리즘 단점
    • 거부된 요청의 타임스탬프도 보관하기 때문에, 다량의 메모리를 사용한다.

 

이동 윈도 카운터 알고리즘(sliding window counter)

  • 고정 윈도 카운터 알고리즘 + 이동 윈도 로깅 알고리즘
  • 동작 원리 
    • 처리율 제한 장치 한도 분당 7개 
    • 현재 1분간 요청 수 + 직전 1분간 요청 수 X 이동 윈도와 직전 1분이 겹치는 비율
    • 현재 윈도에 있는 요청 = 3 + 5 X 70% = 6.5개(내림 6개)

 

  • 이동 윈도 카운터 알고리즘 장점
    • 이전 시간대 평균 처리율에 따라 현재 윈도 상태를 계산해서 짧은 시간에 몰리는 트래픽을 잘 대응할 수 있다.
  • 이동 윈도 카운터 알고리즘 단점
    • 직전 시간대에 도착한 요청이 균등하게 분포되어 있다는 가정에서 추정치를 계산해서 다소 느슨하다.

 

개략적인 아키텍처

  • 얼마나 많은 요청이 접수되었는지를 추적할 수 있는 카운터를 추적 대상별로 둔다.
    • 사용자별로 둘 것인지?
    • IP 주소별
    • API 엔드포인트나 서비스 단위
  • 카운터 값이 한도를 넘으면 다음 요청은 거부하기
  • 카운터 보관 장소는?
    • 데이터베이스는 디스크 접근 때문에 느리다.
    • 메모리 상에서 동작하는 캐시가 빠르고 시간에 기반한 만료 정책을 지원한다.
      • Redis : 처리율 제한 장치를 구현할 때 자주 사용되는 메모리 기반 저장장치. (INCR, EXPIRE 두가지 명령어 지원)
      • INCR : 메모리에 저장된 카운터의 값을 1 증가
      • EXPIRE : 카운터에 타임아웃 값을 설정한다. 설정된 시간이 지나면 카운터 자동 삭제

  • 동작 원리
    • 클라이언트가 처리율 제한 미들웨어에게 요청 전송
    • 처리율 제한 미들웨어는 Redis의 지정 버킷에서 카운터를 가져와서 한도에 도달했는지 검사 
      • 한도 도달 -> 요청 거부
      • 한도 도달 X -> 요청이 API 서버에 전달, 미들웨어는 카운터 값 증가 후 다시 레디스 저장

3단계 : 상세 설계

  • 처리율 제한 규칙은 어디서 만들고 어디서 저장되는지?
  • 처리가 제한된 요청은 어떻게 처리하는지?

 

처리율 제한 규칙

  • 설정 파일(configuration file) 형태로 디스크에 저장
  • 예제 : 시스템이 처리할 수 있는 마케팅 메시지 최대치를 하루 5개로 제한

  • 예제 : 클라이언트가 분당 5회 이상 로그인할 수 없도록 제한

 

 

처리율 한도 초과 트래픽 처리

  • 한도 제한 후 API는 HTTP status (429: too many requests) 응답을 클라이언트에게 보낸다.
  • 또는 큐에 보관할 수도 있다. 

 

처리율 제한 장치가 사용하는 HTTP 헤더

  • 클라이언트가 자기 요청이 처리율 제한에 걸리고 있는지 어떻게 감지?
    • 처리율 제한 장치는 HTTP 응답 헤더(Response header)를 클라이언트에게 보낸다.
      • X-Ratelimit-Remaining : 윈도 내에 남은 처리 가능 요청의 수
      • X-Ratelimit-Limit : 매 윈도마다 클라이언트가 전송할 수 있는 요청의 수
      • X-Ratelimit-Retry-After : 한도 제한에 걸리지 않으려면 몇 초 뒤에 요청을 다시 보내야 하는지 알림

 

  • 처리율 제한 규칙은 디스크에 보관. 작업 프로세스는 수시로 규칙을 디스크에서 읽어 캐시에 저장
  • 클라이언트가 요청을 서버에 보내기 전 처리율 제한 미들웨어에 도달
  • 처리율 제한 미듈웨어는 제한 규칙을 캐시에 가져와서, Redis 캐시에서(카운터, 마지막 요청의 타임스탬프) 정보를 가져온다.
    • 처리율 제한 걸림 : 클라이언트에게 429 too many request error 
    • 처리율 제한에 걸리지 않음 : API 서버로 요청 전달

 

분산 환경에서의 처리율 제한 장치의 구현

  • 여러 대의 서버와 병렬 스레드에서 2가지 문제 해결 필수
    • 경쟁 조건(race condition)
    • 동기화(synchronization)

 

경쟁 조건

  • 병행성이 심한 경우, 경쟁 조건 이슈 발생 확률이 있다.
  • 레디스의 저장된 카운터가 3일 경우, 각각 counter(3) + 1으로 4가 레디스에 기록되야 한다. 하지만 counter가 5가 되어 레디스에 기록되어야 한다.
  • 경쟁 조건 문제를 해결하기 위해서 락(lock)을 사용한다. 하지만 시스템의 성능을 많이 떨어뜨리는 문제가 생긴다.
  • 아래의 설계에서는 락 대신 해결책 2가지 존재
    • 루아 스크립트(Lua script) : 명령형/절차적 언어
    • 정렬 집합(sorted set)이라 불리는 레디스 자료구조 사용

 

동기화 이슈

  • 처리율 제한 장치 서버를 여러 대 둘 경우 동기화 필요
  • 웹 계층은 무상태이기 때문에 각기 다른 제한 장치로 요청 전송 가능
  • 동기화 처리를 하지 않으면, 제한 장치 1이 클라이언트 2에 대한 정보가 없기 때문에, 처리율 제한을 제대로 수행할 수 없다.

  • 동기화 이슈 해결
    • 고정 세션(sticky session) 사용 : 같은 클라이언트부터의 요청을 항상 같은 처리율 제한 장치로 보낼 수 있도록 한다. 하지만 확장 불가능, 유연성 저하로 추천 X
    • 중앙 집중형 데이터 저장소(Redis) 사용 

 

 

성능 최적화

  • 데이터센터 지원 고려 : 데이터 센터에서 멀리 떨어진 사용자를 지원하다가 지연시간(latency)가 증가할 위험이 있다.
  • 제한 장치 간 데이터 동기화에 최종 일관성 모델(eventual consistency model) 사용 

 

모니터링

  • 채택된 처리율 제한 알고리즘이 효과적인지 확인
  • 정의한 처리율 제한 규칙이 효과적인지 확인

4단계 : 마무리

  • 처리율 제한 알고리즘 : 토큰 버킷, 누출 버킷, 고정 윈도 카운터, 이동 윈도 로그, 이동 윈도 카운터
  • 경성(hard) 또는 연성(soft) 처리율 제한 : 경성(임계치 절대 넘기 불가능), 연성(임계치를 잠시 넘을 수 있다.)
  • 다양한 계층에서의 처리율 제한 : 애플리케이션 계층 말고 다른 OSI 7 계층에서도 사용 가능
  • 처리율 제한 회피 방법
    • 클라이언트 측 캐시를 사용하여 API 호출 횟수 감소
    • 처리율 제한 임계치 이해하기. 짧은 시간동안 많은 메시지 보내지 않기
    • 예외나 에러 처리 코드를 도입하여 복구 용이
    • 재시도 로직을 구현할 때 충분한 백오프 시간 두기

효과적 면접을 위한 4단계 접근법

 

1단계 : 문제 이해 및 설계 범위 확정

  • 생각 없이 바로 답을 내놓지 않는다.
  • 요구사항을 정확히 이해하고, 생각하고 질문하여 답을 한다.
  • 필요 질문 예시 
    • 구체적으로 어느 기능을 만드는지?
    • 제품의 사용자 수는?
    • 회사의 규모는 얼마나 빨리 커질 것으로 예상하는지? x 년 이후의 규모는?
    • 회사의 기술 스택은 무엇인지?
  • 예제
    • 모바일 앱, 웹 앱 어느쪽 지원?
    • 가장 중요한 기능은?
    • 뉴스 피드는 시간 역순인지, 정렬기준이 있는지
    • 한 사용자가 최대 몇명의 사용자와 친구를 맺을 수 있는지?
    • 사이트로 오는 트래픽 규모는?
    • 피드에 이미지, 비디오도 가능한지? 아니면 그저 텍스트인지?

 

2단계 : 개략적인 설계안 제시 및 동의 구하기

  • 핵심 컴포넌트를 포함하는 다이어그램을 그린다.
    • 클라이언트(모바일/웹), API, 웹, 서버, 데이터 저장소, 캐시, CDN, 메시지 큐 등등
  • 최초 설계안이 시스템 규모에 관계된 제약사항을 만족하는지 개략적으로 계산하기
  • 예제 - 뉴스 피드 시스템 설계
    • 피드 발행 : 사용자가 포스트를 올리면 관련된 데이터가 캐시/데이터베이스에 기록되고, 해당 사용자의 친구 뉴스피드에 뜬다.
    • 피드 생성 : 어떤 사용자의 뉴스 피드는 해당 사용자 친구들의 포스트를 시간 역순으로 정렬하여 만든다.
     

 

 

 

3단계 : 상세 설계

  • 설계 대상 컴포넌트 사이의 우선순위 정하기
  • 시스템 성능 특성에 대한 질문에는?
    • 시스템의 병목 구간이나 자원 요구량 추정치에 초점 맞추기
  • 단축 URL 생성기 설계 문제에는?
    • 해시 함수 설계를 구체적으로 설명하기
  • 채팅 시스템에 대한 문제는?
    • 어떻게 하면 지연시간(latency)를 줄이고 사용자의 온/오프라인 상태를 표시할 것인지
  • 예제 - 뉴스 피드 시스템
    • 피드 발행
    • 뉴스 피드 가져오기

 

4단계 : 마무리

  • 오류가 발생하면 무슨 일이 생기는지(서버 오류, 네트워크 장애 등)
  • 운영 이슈 논의 - 메트릭은 어떻게 수집하고 모니터링과 로그는 어떻게? 배포는 어떻게 할 것인지?
  • 규모 확장을 어떻게 해나갈 것인지? 

개략적인 규모 추정을 위해서는 규모 확장성 표현에 능숙해야 한다.

  • 2의 제곱수
  • 응답 지연(latency) 값
  • 고가용성

 

2의 제곱수

  • 데이터 최소 단위 : 1 byte = 8bit
  • ASCII 문자 하나 메모리 크기 : 1 byte

 

모든 프로그래머가 알아야 하는 응답지연 값

  • 메모리는 빠르지만 디스크는 아직 느리다.
  • 디스크 탐색(seek) 가능한 피하기
  • 단순한 압축 알고리즘은 속도가 빠르다.
  • 데이터를 인터넷으로 전송하기 전에 가능하면 압축하기
  • 데이터 센터는 여러 지역에 분산되어 있고, 센터들 간에 데이터를 주고 받는 시간은 꽤 걸린다.

 

가용성에 관계된 수치들

  • 고가용성(high availability) : 시스템이 오랜 시간동안 지속적으로 중단 없이 운영될 수 있는 능력
  • SLA(Service Level Agreement) : 서비스 사업자(service provider)와 고객 사이에 맺어진 합의
    • 서비스 사업자가 제공하는 서비스의 가용시간(uptime)이 공식적으로 기술되어 있다.

 

예제 : 트위터 QPS와 저장소 요구량 추정

  • 월간 능동 사용자 3억명
  • 50% 사용자가 트위터를 매일 사용
  • 평균적으로 각 사용자는 매일 2건의 트윗을 올린다.
  • 미디어를 포함하는 트윗은 10%
  • 데이터 5년 보관

 

  • 미디어 저장을 위한 저장소 요구량
    • 1.5억 X 2 X 10% X 1MB = 30TB
    • 5년 미디어 보관을 위한 저장소 요구량 : 30TB X 365 X 5 = 약 55PB

단일 서버

  • 웹 앱, 데이터베이스, 캐시 등이 전부 서버 한 대에서 실행된다.

 

사용자 요청 처리 흐름

1. 사용자는 도메인 이름(api.mysite.com)을 이용해서 웹사이트에 접속한다. 이 접속을 위해서는 도메인 이름을 DNS에 질의하여 IP 주소로 변환하는 과정이 필요하다.

2. DNS 조회 결과로 IP 주소가 반환된다. (웹 서버의 주소)

3. 해당 IP 주소로 HTTP 요청이 웹 서버에 전달된다.

4. 요청을 받은 웹 서버는 HTML 페이지나 JSON 형태의 응답을 반환한다.

 

두 가지 종류의 단말으로부터 요청

  • 웹 애플리케이션
    • 서버 구현용 언어(자바, 파이썬) : 비즈니스 로직, 데이터 저장 처리
    • 클라이언트 구현용 언어(HTML, JS) : 프레젠테이션용
  • 모바일 앱
    • HTTP 프로토콜 이용 : 모바일 앱과 웹 서버 간 통신 
    • JSON 데이터 포맷 사용

 


데이터베이스

  • 사용자가 증가할 경우, 하나의 서버로는 부족해진다. 여러 서버를 두어야 한다.
  • 아래 그림은 웹/모바일 트래픽 처리(웹 계층)와 데이터베이스(데이터 계층) 용을 따로 분리했다.

 

 

데이터베이스의 종류

  • 관계형 데이터베이스(Relational Database)
    • MySQL, Oracle, PostgreSQL
    • 테이블, 열, 칼럼
    • join 지원
  • 비-관계형 데이터베이스(NoSQL)
    • CouchDB, Neo4j, Cassandra, HBase, Amazon DynamoDB
    • NoSQL의 4가지 분류
      • 키-값 저장소(key-value store) 
      • 그래프 저장소(graph store)
      • 칼럼 저장소(column store)
      • 문서 저장소(document store)
    • join 지원 X

 

NoSQL 활용 예시

  • 아주 낮은 응답 지연시간(latency) 요구되는 경우
  • 다루는 데이터가 비정형(unstructured)이라 관계형 데이터가 아니다.
  • 데이터(JSON, YAML, XML 등)를 직렬화하거나(serialize) 역직렬화(deserial-ize)할 수 있기만 하면 된다.
  • 아주 많은 양의 데이터를 저장해야 하는 경우

수직적 규모 확장 vs 수평적 규모 확장

  • 수직적 확장 : 서버로 유입되는 트래픽 양이 적을 때

 

스케일 업(scale up)

  • 수직적 규모 확장 프로세스
  • 서버에 고사양 자원(더 좋은 CPU, 더 많은 RAM)을 추가하는 행위
  • 단점
    • 확장의 한계, 한 대의 서버에 CPU나 메모리를 무한대로 증설 불가능
    • 장애에 대한 자동복구나 다중화 방안을 제시하지 않는다. -> 장애 시 웹 사이트의 완전 종료

 

스케일 아웃(scale out)

  • 수평적 규모 확장 프로세스
  • 더 많은 서버를 추가하여 성능 개선하는 행위
  • 수직적 규모 확장 프로세스의 단점으로 대규모 애플리케이션을 지원하는 데 수평적 규모 확장 프로세스를 사용한다.

 

로드밸런서(load balancer)

  • 부하 분산 집합에 속한 웹 서버들에게 트래픽 부하를 고르게 분산하는 역할
  • 너무 많은 사용자가 접속하거나 웹 서버가 한게 상황에 도달하면 응답 속도가 느려지거나 서버 접속이 불가능해질 수 있다. -> 로드밸런서 도입
  • 사용자는 로드밸런서의 공개 IP 주소로 접속해 웹 서버는 클라이언트의 접속을 직접 처리하지 않는다.
  • 보안 향상을 위해 서버 간 통신에는 사설 IP 주소를 이용함으로싸, 인터넷을 통해서 접속하지 못하게 한다. 로드밸런서는 웹 서버와 통신하기 위해 이 사설 주소를 사용한다.
  • 로드 밸런서는 자동적으로 트래픽을 분산해주기 때문에 서버1이 다운되면, 모든 트래픽은 서버2로 전송한다.

 

 

데이터베이스 다중화

  • 데이터 계층의 문제 해결 방법
  • 서버 사이에 주(master)-부(slave) 관계를 설정하고 데이터 원본은 주 서버에, 사본은 부 서버에 저장하는 방식
    • 주 서버 : 쓰기 연산, insert, delete, update
    • 부 서버 : 주 서버의 사본을 전달받고, 읽기 연산만 지원 
  • 대부분의 애플리케이션은 쓰기 연산 < 읽기 연산 
  • 주 서버보다 부 서버의 수가 더 많다.

  • 다중화의 장점 
    • 더 나은 성능 : 주-부 다중화 모델에서 쓰기 연산은 주 데이터베이스 서버로, 읽기 연산은 부 데이터베이스 서버로 분산된다. 병렬로 처리될 수 있는 질의(query) 수가 늘어나 성능이 향상된다.
    • 안정성 : 데이터베이스 서버 가운데 일부가 파괴되도 데이터의 보존이 가능하다. 
    • 가용성 : 여러 지역에 데이터를 복사해서, 하나의 데이터가 장애가 발생해도 다른 서버에서 복구 가능
    • 주 서버가 다운될 경우?
      • 부 서버가 일시적으로 주 서버가 되고, 부 서버에 보관되었던 데이터가 최신 상태가 아닐 수 있을 경우에는 복구 스크립트(recovery script)를 돌려서 부족한 데이터를 추가해준다.

  • 사용자는 DNS에서 로드밸런서의 public IP를 발급
  • 사용자는 해당 IP 주소로 로드밸런서에 접속
  • HTTP 요청은 서버 1 or 서버 2로 전달
  • 웹 서버는 읽기 연산을 부 데이터베이스 서버로
  • 웹 서버는 쓰기 연산을 주 데이터 베이스로 전달

캐시(Cache)

  • 실행이 오래걸리는 연산이나 자주 참조되는 데이터를 메모리 안에 두고, 요청이 보다 빨리 처리될 수 있도록 하는 저장소
  • 애플리케이션의 성능은 데이터 베이스를 얼마나 자주 호출하느냐에 따라 크게 달라진다.
  • 응답 시간 개선 : 캐시(cache)를 붙이고 정적 콘텐츠를 콘텐츠 전송 네트워크(Content Delivery Network, CDN)으로 이동

 

캐시 계층(Cache tier)

  • 데이터가 잠시 보관되는 곳. 데이터베이스보다 빠르다.
  • 데이터베이스 부하를 줄일 수 있고, 캐시 계층의 규모를 독립적으로 확장 가능
  • 읽기 주도형 캐시 전략(read-through caching strategy) : 웹 서버는 캐시에 응답이 있는지 확인 후, 데이터가 있으면 반환한다. 없으면 데이터베이스에 query로 데이터를 찾아 캐시에 저장한 후 클라이언트에 반환

  • 캐시 서버 이용 방법 - memcached API 사용
SECONDS = 1
cache.set('myKey', 'hi there', 3600 * SECONDS)
cache.get('myKey')

 

캐시 사용 주의 사항

  • 참조가 빈번하게 일어나는 경우
  • 캐시는 휘발성 메모리이기 때문에, 중요 데이터는 지속성 저장소(persistent data store)에 저장
  • 캐시 만료 기준 : 적절한 정책으로 만료 기한을 설정한다.
  • 일관성(consistency) : 데이터 저장소의 원본과 캐시 내의 사본이 같은지? 
    • 저장소의 원본을 갱신하는 연산 = 캐시를 갱신하는 연산이 단일 트랜잭션으로 처리되어야 한다.
  • 캐시 서버를 하나만 둘 경우, 단일 장애 지점(Single Point of Failure, SPOF)이 되어버릴 수 있다. 전체 시스템이 동작 중지가 될 수 있기 때문에 여러 지역에 걸쳐 캐시 서버를 분산해야 한다.
  • 캐시 메모리를 너무 작게 잡을 경우, 데이터가 캐시에 자주 밀려나는 문제가 생긴다. 이 경우 캐시 메모리를 과할당(overprovision)한다.
  • 데이터 방출 정책을 적절하게 적용
    • LRU(Least Recently Used) : 사용이 가장 오래된 데이터부터 내보낸다.
    • LFU(Least Frequently Used) : 사용 빈도가 가장 낮은 데이터부터 내보낸다.
    • FIFO(First In First Out) : 가장 먼저 들어온 데이터를 가장 먼저 내보낸다. 

콘텐츠 전송 네트워크(CDN)

  • CDN : 정적 콘텐츠를 전송하는 데 쓰이는 지리적으로 분산된 서버의 네트워크
  • 동적 콘텐츠 캐싱 : request path, query string, request header, 쿠키 등의 정보에 기반하여 HTML 페이지를 캐시하는 것

 

CDN 동작 과정

  • 사용자가 웹사이트 방문시, 사용자에게 가까운 CDN 서버가 정적 콘텐츠를 전달한다.
  • 정적 콘텐츠 : js, css, 이미지 등등

1. 사용자 A 가 이미지 URL으로 image.png에 접근한다. (URL 도메인은 CDN 서비스 사업자가 제공)

2. CDN 서버의 캐시에 해당 이미지가 없는 경우, 서버는 원본 서버에 요청하여 파일을 가져온다.

3. 원본 서버가 파일을 CDN 서버에 반환. (응답 HTTP header에 파일이 언제까지 캐시될 수 있는지 TTL(Time-To-Live)값이 있다.)

4. CDN 서버는 파일을 캐시하고 사용자 A에게 반환. 이미지는 TTL에 명시된 시간까지 캐시

5. 사용자 B가 같은 이미지에 대한 요청을 CDN 서버에 전송

6. 만료되지 않은 이미지에 대한 요청은 캐시를 통해 처리된다.

 

 

CDN 사용 고려 사항

  • 비용 : CDN은 보통 제 3 사업자(third-party providers)가 운영한다. CDN에 들어가고 나오는 데이터의 전송 양에 따라 비용을 내기 때문에, 자주 사용되지 않는 콘텐츠는 CDN에서 뺸다.
  • 적절한 만료 시한 설정 : 시의성이 중요한(time-sensitive) 콘텐츠는 만료 기간을 잘 정해야 한다. 
  • CDN 장애에 대한 대처 방안 : CDN 자체가 죽을 경우, 웹사이트/애플리케이션이 어떻게 동작해야 하는지 고려하기 -> 원본 서버로부터 직접 콘텐츠를 가져오도록 클라이언트 구성
  • 콘텐츠 무효화(invalidation) 방법 : 아직 만료되지 않은 콘텐츠를 CDN에서 제거할 때 사용
    • CDN 서비스에서 제공하는 API 사용
    • 콘텐츠의 다른 버전을 서비스하도록 오브젝트 버저닝(object versioning) 이용 -> image.png?v=2

 

CDN + Cache

  • CDN : 정적 콘텐츠는 웹 서버를 통해 서비스하지 않고, 성능 개선
  • Cache : 데이터베이스 부하 감소 


무상태(stateless) 웹 계층

  • 웹 계층을 수평적으로 확장하기 위해서는 상태 정보(사용자 세션 데이터 등등)를 웹 계층에서 제거해야 한다.
  • 무상태 웹 계층 : 상태 정보를 RDBMS, NoSQL 같은 지속성 저장소에 보관하고, 필요할 때 가져온다. 

 

상태 정보 의존적인 아키텍처

  • 상태 정보 보관 서버 : 클라이언트 정보(상태 유지)로 요청들 사이에 공유
  • 사용자 A의 정보는 서버 1에 저장되어 있고, 사용자 A를 인증하려면 HTTP 요청이 서버 1으로 전송해야 한다.
  • 같은 클라이언트의 요청은 같은 서버로 전송되어야 하는 문제점 때문에 로드밸런서의 고정 세션(sticky session) 기능을 사용하지만, 부담을 줄 수 있고 확장성에 좋지 않다.

 

무상태 아키텍처

  • 사용자로부터 HTTP 요청은 어떤 웹 서버로도 전달될 수 있기 때문에 상태 정보가 필요한 경우 공유 저장소(shared storage)로부터 데이터를 가져온다.
  • 단순한 구조, 안정적, 규모 확장 용이

 

기존 설계 + 무상태 아키텍처


데이터 센터

  • 여러 지역의 사용자가 쾌적하게 사용할 수 있도록 지원해야 할 때 사용한다.
  • 지리적 라우팅 : 사용자에게 가장 가까운 데이터 센터로 안내되는 것
  • geoDNS는 사용자의 위치에 따라 도메인 이름을 어떤 IP 주소로 변환할지 결정해주는 DNS 서비스

 

다중 데이터센터 아키텍처 고려사항

  • 트래픽 우회 : 올바른 데이터 센터로 트래픽 보내는 방법 찾기
  • 데이터 동기화(synchronization) 
  • 테스트와 배포 : 여러 위치에서 테스트 해보기

메시지 큐(message queue)

  • 시스템을 더 큰 규모로 확장하기 위해 시스템의 컴포넌트를 분리하여 각각 독립적으로 확장될 수 있도록 해야 한다.
  • 메시지의 무손실(durability)을 보장하는, 비동기 통신을 지원하는 컴포넌트
  • 메시지의 버퍼 역할을 하며, 비동기적으로 전송
  • 입력서비스(생산자/발행자)가 메시지를 만들어 메시지 큐에 발행(publish)
    • 소비자 프로세스가 다운되도 메시지 발행 가능
  • 큐에는 서비스 or 서버(구독자/소비자)가 연결되어 있는데, 메시지를 받아 그에 맞는 동작을 수행한다.
    • 생산자 서비스가 가용한 상태가 아니어도 메시지 수신 가능

 

메시지 큐 사용 예시

  • 사진 보정 애플리케이션에서 보정은 시간이 오래 걸릴 수 있는 프로세스이다. -> 비동기적으로 처리하기
  • 웹 서버는 사진 보정 작업을 메시지 큐에 넣고
  • 사진 보정 작업 프로세스는 메시지 큐에서 꺼내면서 비동기적으로 완료한다.

로그, 메트릭 그리고 자동화

  • 로그
    • 에러 로그 모니터링은 시스템의 오류와 문제들을 쉽게 찾아낼 수 있다. 
  • 메트릭
    • 잘 수집된 메트릭은 사업 현황에 유용한 정보를 얻을 수 있고, 시스템의 상태를 쉽게 파악 가능하다.
    • 호스트 단위 메트릭 : CPU, 메모리, 디스크 I/O
    • 종합 메트릭 : 데이터베이스 계층의 성능, 캐시 계층의 성능
    • 핵심 비즈니스 메트릭 : 일별 능동 사용자, 수익, 재방문
  • 자동화
    • 시스템이 복잡해질 때, 개발 생산성을 높이기 위해 사용

 

기존 설계 + 메시지 큐, 로그, 메트릭, 자동화


데이터베이스 규모 확장

  • 수직적 규모 확장법, 수평적 규모 확장법

 

수직적 확장(Scale up)

  • 고성능 자원(CPU, RAM, 디스크) 을 증설하는 방법

 

수평적 확장(sharding)

  • 더 많은 서버를 추가해 성능 향상
  • 샤딩 : 대규모 데이터베이스를 샤드(shard) 단위로 분할하는 기술
  • 샤드는 같은 스키마를 사용하지만, 보관되는 데이터는 중복 X

 

샤딩 전략 고려사항

  • 샤딩 키를 어떻게 정하는지?
  • 데이터 재샤딩
    • 데이터가 너무 많아 하나의 샤드로 불가능한 경우
    • 샤드 간 데이터 분포가 균등하지 않은 경우 -> 샤드 소진 현상 발생 -> 샤드 키 계산 함수를 변경한다, 안정 해시 기법
  • 유명인사 문제 : 특정 샤드에 질의가 집중되어 서버에 과부하 되는 문제
    • 유명인사 각각에 샤드를 하나씩 할당하거나 더 잘개 쪼개주어야 한다.
  • 조인과 비정규화 
    • 하나의 DB를 여러 샤드로 쪼개면, JOIN이 어렵다는 문제가 생긴다.
    • DB를 비정규화 하여 하나의 테이블에서 질의가 수행될 수 있도록 해준다.

 

기존 설계 + RDBMS가 필요 없는 기능 -> NoSQL 이전


백만 사용자, 그리고 그 이상

  • 웹 계층은 무상태 계층
  • 모든 계층에 다중화 도입
  • 가능한 많은 데이터 캐시
  • 여러 데이터 센터 지원
  • 정적 콘텐츠는 CDN으로 서비스
  • 데이터 계층은 샤딩을 통해 규모를 확장하기
  • 각 계층은 독립적 서비스로 분할
  • 시스템을 지속적으로 모니터링하고, 자동화 도구를 활용하기

+ Recent posts