[가상 면접 사례로 배우는 대규모 시스템 설계 기초] 1장 사용자 수에 따른 규모 확장성

Hocaron·2022년 6월 2일
3

단일 서버

  1. 사용자는 도메인 이름을 이용하여 웹사이트에 접속한다. 이 접속을 위해서는 도메인 이름을 도메인 이름 서비스에 질의하여 IP 주소로 변환하는 과정이 필요하다.
  2. DNS 조회로 웹 서버의 IP 주소가 반환된다.
  3. 해당 IP 주소로 HTTP 요청이 전달된다.
  4. 요청을 받은 웹 서버는 HTML 페이지나 JSON 형태의 응답을 반환한다.

데이터베이스

사용자가 늘면 서버 하나로는 충분하지 않아서 여러 서버를 두어야 한다. 하나는 웹/모바일 트래픽 처리 용도고, 다른 하나는 데이터베이스용이다.

데이터베이스는 크게 RDBMS, NoSQL 2가지로 나눌 수 있다. 대체적으로는 RDBMS로 해결할 수 있고 적절한 상황이지만, 아래와 같은 경우에는 NoSQL이 적절한 선택일 수 있다.

  • 아주 낮은 응답 지연시간이 요구됨
  • 다루는 데이터가 비정형이라 관계형 데이터가 아님
  • 데이터를 직렬화하거나 역직렬화할 수 있기만 하면 됨
  • 아주 많은 양의 데이터를 저장할 필요가 있음

🤔 Graph Database(Neo4J)
데이터가 서로 복잡하게 연결되어 있고, 그 데이터가 다른 데이터와 어떻게 연결되고 참조하는지를 표현해야 할때 적용하면 효과적이다. 전통적인 RDBMS에서는 다대다의 관계이 경우를 말한다. 실시간 추천엔진, 콘텐츠 및 자산관리 시스템, ID 및 엑세스 관리 시스템, 규정 준수 및 위험관리 솔루션, 소셜네트워크와 같은 관계를 보고자 할때 사용한다.
수많은 조인을 줄여줌

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

스케일 업(scale up): 수직적 규모 확장은 서버에 고사양 자원(더 좋은 CPU, 더 많은 RAM)을 추가하는 것

스케일 아웃(scale out): 수평적 규모 확장은 더 많은 서버를 추가하여 성능을 개선하는 행위

무엇이 더 적절할까?

서버로 유입되는 트래픽의 양이 적을 때는 수직적 확장이 좋은 선택이며, 이 방법의 가장 큰 장점은 단순함이다. 그러나 스케일 업에는 심각한 단점이 존재한다.

  1. 한 대의 서버에 CPU나 메모리를 무한대로 증성할 방법은 없다.
  2. 장애에 대한 자동복구(failover) 방안이나 다중화 방안을 제시하지 않는다. 서버에 장애가 발생하면 웹사이트/앱은 완전히 중단된다.

위와 같은 단점 때문에, 대규모 애플리케이션을 지원하는 데는 수평적 규모 확장법이 보다 적절하다.

로드밸런서

사용자는 로드밸런서의 Public IP로 접속한다. 서버 간 통신에는 Private IP가 이용된다. 위처럼 스케일 아웃을 통해서 서버를 여러 대로 늘리게 되면 no failover도 해소할 수 있고, 웹 계층의 가용성은 향상된다.

Elastic Load Balancing의 작동 방식
로드 밸런서는 클라이언트에서 오는 트래픽을 허용하고, 하나 이상의 가용 영역에서 등록된 대상(예: EC2 인스턴스)으로 요청을 라우팅합니다. 또한, 로드 밸런서는 등록된 대상의 상태를 모니터링하고 정상 대상으로만 트래픽이 라우팅되도록 합니다. 로드 밸런서가 비정상 대상을 감지하면, 해당 대상으로 트래픽 라우팅을 중단합니다. 그런 다음 대상이 다시 정상으로 감지되면 트래픽을 해당 대상으로 다시 라우팅합니다.
하나 이상의 리스너를 지정하여 들어오는 트래픽을 허용하도록 로드 밸런서를 구성합니다. 리스너는 연결 요청을 확인하는 프로세스입니다. 클라이언트와 로드 밸런서 간의 연결을 위한 프로토콜 및 포트 번호로 구성됩니다. 마찬가지로 로드 밸런서와 대상 간의 연결을 위한 프로토콜 및 포트 번호로 구성됩니다.

데이터베이스 다중화

데이터베이스를 Master-Slave로 나누는데 쓰기 연산Master에서만 지원하고, 읽기 연산Slave에서만 지원한다. 대부분의 애플리케이션에서는 쓰기 작업 보다는 읽기 작업이 훨씬 많기 때문에 Slave 데이터베이스가 훨씬 많다.

예를 들면,

  • select 은 slaveDB
  • update master DB

데이터베이스 다중화의 장점

  • 더 나은 성능 : 모든 데이터 변경 연산은 Master 서버로만 전달되는 반면 읽기 연산은 Slave 데이터베이스 서버들로 분산된다. 병렬로 처리될 수 있는 쿼리의 수가 늘어나므로 성능이 좋아진다.
  • 안전성: 데이터베이스 서버 가운데 일부가 파괴되어도 데이터는 보존될 것이다. 데이터를 지역적으로 떨어진 여러 장소에 다중화 시켜놓을 수 있기 때문이다.
  • 가용성: 데이터를 여러 지역에 복제해 둠으로써, 하나의 데이터베이스 서버에 장애가 발생하더라도 다른 서버에 있는 데이터를 가져와 계속 서비스할 수 있게 된다.

  1. 사용자는 DNS로부터 로드밸런서의 공개 IP 주소를 받는다.
  2. 사용자는 해당 IP 주소를 사용해 로드밸런서에 접속한다.
  3. HTTP 요청은 서버 1이나 서버 2로 전송된다.
  4. 웹 서버는 사용자의 데이터를 부 데이터베이스 서버에서 읽는다.
  5. 웹 서버는 데이터 변경 연산은 주 데이터베이스로 전달한다.

캐시

캐시는 값 비싼 연산 결과 또는 자주 참조되는 데이터를 메모리 안에 두고, 뒤이은 요청이 보다 빨리 처리될 수 있도록 하는 저장소이다. 애플리케이션의 성능은 데이터베이스를 얼마나 자주 호출하느냐에 크게 좌우되는데, 캐시는 그런 문제를 완화할 수 있다.

캐시 계층

캐시 계층은 데이터가 잠시 보관되는 곳으로 데이터베이스보다 훨씬 빠르다.

  1. 요청을 받은 웹 서버는 캐시에 응답이 저장되어 있는지를 확인한다.
  2. 만일 저장되어 있다면 해당 데이터를 클라이언트에 반환한다.
  3. 없는 경우에는 데이터베이스 쿼리를 통해 데이터를 찾아 캐시에 저장한 뒤 클라이언트에 반환한다.

캐시 사용 시 유의할 점

  • 캐시는 갱신은 자주 일어나지 않지만 참조는 빈번하게 일어난다면 고려해볼만 하다.
  • 영속적으로 보관할 데이터를 캐시에 두는 것은 바람직하지 않다.
    • 캐시는 데이터를 휘발성 메모리에 두기 때문에, 캐시 서버가 재시작되면 모든 데이터는 사라진다.
  • 만료된 데이터는 캐시에서 삭제되어야 한다.
    • 만료 기한에 대한 정책을 마련하는 것이 좋다.
    • 만료 기한이 너무 짧으면, 데이터베이스를 너무 자주 읽게 되며, 너무 길면 원본과 차이가 날 가능성이 높아진다.
  • 데이터 저장소의 원본과 캐시 내의 사본 일관성을 확인해야 한다.
  • 캐시 서버를 한 대만 두는 경우 해당 서버는 단일 장애 지점(SPOF)이 되어 버릴 수 있다.
  • 캐시 메모리 크기는 너무 크지도 작지도 않게 적절하게 잡아야 한다.
  • 캐시가 꽉 차버리면 LRU, LFU, FIFO 같은 정책들을 사용해서 사용해야 한다.

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

CDN은 정적 콘텐츠를 전송하는 데 쓰이는, 지리적으로 분산된 서버의 네트워크이다. 이미지, 비디오, CSS, JavaScript 파일 등을 캐시할 수 있다.


  1. 사용자 A가 이미지 URL을 이용해 image.png에 접근한다.
  2. CDN 서버의 캐시에 해당 이미지가 없는 경우, 서버는 원본 서버에 요청하여 파일을 가져온다.
  3. 원본 서버가 파일을 CDN 서버에 반환한다.
  4. CDN 서버는 파일을 캐시하고 사용자 A에게 반환한다.
  5. 사용자 B가 같은 이미지에 대한 요청을 CDN 서버에 전송한다.
  6. 만료되지 않은 이미지에 대한 요청은 캐시를 통해 처리된다.

  1. 정적 컨텐츠(JS, CSS, 이미지 등)는 더 이상 웹 서버를 통해 서비스하지 않으며, CDN을 통해 제공하여 더 나은 성능을 보장합니다.
  2. 캐시가 데이터베이스 부하를 줄어줍니다.

무상태(stateless) 웹 계층

이제는 웹 계층을 수평적으로 확장하는 방법을 고민해보자.
HTTP 같은 경우는 stateless 하기 때문에 세션 같은 정보는 저장을 해두어야 한다. 세션 정보를 서버 내부 메모리와 같은 곳에 사용할 수 있는데, 그런데 만약 서버가 여러 대라면 어떻게 세션 정보를 유지할 수 있을까?

세션은 요청에 대한 context다.

위와 같이 세션 저장소를 서버 내부 메모리를 사용하지 않고, RDBMS or Redis/Memcached와 같은 외부 공유 저장소이다.

무상태 Stateless
클라이언트와 서버 관계에서 서버가 클라이언트의 상태를 보존하지 않음을 의미한다.
Stateless 구조에서 server는 단순히 요청이 오면 응답을 보내는 역할만 수행하며, 세션 관리는 client에게 책임이 있다. 따라서 이러한 Stateless 구조는 client와의 세션 정보를 기억할 필요가 없으므로, 이러한 정보를 서버에 저장하지 않는다. 
대신 필요에 따라 외부 DB에 저장하여 관리 할 수 있다.
웹서버 통신(http) 특성상 사용자(브라우저)의 이전 상태 client(쿠키) or server(세션) 정보를 기록하지 않는 접속이란 의미입니다.
브라우저가 데이터를 전송할 때마다 연결하고 바로 끊어버리는 방식이다.
장점 : 서버의 확장성이 높기 때문에 대량의 트래픽 발생 시에도 대처를 수월하게 할 수 있다.
단점 : 클라이언트의 요청에 상대적으로 Stateful 보다 더 많은 데이터가 소모된다. (매번 요청할때마다 자신의 부가정보를 줘야 하니까)

상태유지 Stateful
상태 유지라 함은 클라이언트와 서버 관계에서 서버가 클라이언트의 상태를 보존함을 의미한다.
클라이언트의 이전 요청이 서버에 전달되었을 때, 클라이언트의 다음 요청이 이전 요청과 관계가 이어지는 것을 의미한다. 
웹서버가 사용자(브라우저)의 상태 client(쿠기) or server(세션) 정보를 기억하고 있다가 유용한 정보로써 활용한다는 의미한다.
클라이언트에서 다른 클라이언트로, 또는 서버에서 특정 클라이언트로 메시지를 전송할 수 있다.
서버에서 클라이언트 세션을 유지할 필요가 없을 때 서버 리소스를 절약하는 장점이 있다.
Stateful 방식은 하나의 서버가 1만 명의 클라이언트를 처리할 능력이 있을 때 그보다 많은 수의 클라이언트가 몰리면, 이미 연결된 1만 명의 클라이언트 중 일부가 빠져야 다음 클라이언트가 처리된다.
하지만 Stateless 방식은 순간 접속 요청 인원을 기준으로 하므로 많은 클라이언트가 몰려도 할당된 처리량이 끝나면 다음 처리가 가능하다.

데이터 센터

장애가 없는 상황에서 사용자는 가장 가까운 데이터 센터로 안내되는데, 통상 이 절차를 지리적 라우팅이라고 부른다.

만약 데이터 센터 중 하나에 심각한 자애가 발생하면 모든 트래픽은 장애가 없는 데이터 센터로 전송된다.

다중 데이터센터를 만들기 위해서 고려해야 할 조건

  • 트래픽 우회: 올바른 데이터 센터로 트래픽을 보내는 효과적인 방법을 찾아야 합니다.
  • 데이터 동기화: 데이터 센터마다 별도의 데이터베이스를 다중화하기
  • 테스트와 배포: 여러 위치에서 애플리케이션을 테스트 해보고, 자동화 배포가 모든 데이터 센터에 동일하게 설치되도록 하는 것이 중요합니다.

메세지 큐


메세지 큐는 메세지의 무손실, 즉 메세지 큐에 일단 보관된 메세지는 소비자가 꺼낼 떄까지 안전히 보관된다는 특성을 보장하는 비동기 통신을 지원하는 컴포넌트이다. Publish/Producer는 메세지 큐에 발행한다. 큐에는 보통 Consumer/Subscribe가 메세지를 꺼내서 동작을 수행한다.

메세지 큐를 이용하면 서비스 또는 서버 간 결합이 느슨해져서, 규모 확장성이 보장되어야 하는 안정적 애플리케이션을 구성하기 좋다.

메시지 큐가 이벤트 드리븐이랑 같은 맥락인가여..?
수단으로 보자.
글을 쓰고 분리 → pub/sub
MSA에서 회원서비스 쿠폰을 발급하고 싶을 때 → 이벤트 드리븐

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

비지니스의 규모가 커지면 로그, 매트릭, 자동화 도구에 필수적으로 투자해야한다

  • 로그: 에러 모니터링을 위해. 로그를 단일 서비스로 모아주는 도구를 사용할 수도 있음
  • 메트릭: 유용한 정보, 시스템의 현 상태를 파악하기 위해
  • 자동화: 생산성과 안정성을 높이기 위해

데이터베이스의 규모 확장

저장할 데이터가 많아지면 데이터베이스에 대한 부하도 증가한다. 이 때도 애플리케이션 서버처럼 스케일 업, 스케일 아웃 두 가지 방법이 존재한다.

데이터베이스에도 수직적 확장보다는 수평적 확장이 더 적절하다. 수평적 확장에 대표적으로는 샤딩이 있다. 샤딩은 대규모 데이터베이스를 샤드라고 부르는 작은 단위로 분할하는 기술을 말한다.

샤딩 전략을 구현할 때 중요한 것은 샤딩 키 이다. 샤딩 키에 따라서 한 곳으로만 부하가 집중될 수 있고 여러 곳으로 적절히 잘 분산될 수도 있다.

샤딩을 위해서 고려해야 할 조건

  • 데이터의 재 샤딩
    • 데이터가 너무 많아져서 하나의 샤드로는 더 이상 감당하기 어려울 때
    • 샤드 간 데이터 분포가 균등하지 못하여 어떤 샤드에 할당된 공간 소모가 다른 샤드에 비해 빨리 진행될 때
  • 유명 인사: 핫스팟 키 문제라고도 부르는데, 특정 샤드에 쿼리가 집중되어 서버에 과부하가 걸리는 문제
  • 조인과 비정규화
    일단 하나의 데이터베이스를 여러 샤드 서버로 쪼개고 나면, 여러 샤드에 걸친 데이터를 조인하기가 힘들어진다. 이를 해결하는 한 가지 방법은 데이터베이스를 비정규화하여 하나의 테이블에서 쿼리가 수행될 수 있도록 하는 것이다.

🤔 샤딩과 파티셔닝의 차이점
파티셔닝이란 퍼포먼스(performance), 가용성(availability) 또는 정비용이성(maintainability)를 목적으로 당신의 논리적인 데이터 엘리먼트들을 다수의 엔티티(table)로 쪼개는 행위를 뜻하는 일반적인 용어이다.
샤딩수평 파티셔닝(horizontal partitioning)과 동일하다. 데이터베이스를 샤딩하게 되면 기존에 하나로 구성될 스키마를 다수의 복제본으로 구성하고 각각의 샤드에 어떤 데이터가 저장될지를 샤드키를 기준으로 분리한다. 예를 들면, 나는 고객의 데이터베이스를 CustomerId를 샤드키로 사용하여 샤딩하기로 하였다. 0 ~ 10000 번 고객의 정보는 하나의 샤드에 저장하고 10001 ~ 20000 번 고객의 정보는 다른 샤드에 저장하기로 하였다. DBA는 데이터 엑세스 패턴과 저장 공간 이슈(로드의 적절한 분산 , 데이터의 균등한 저장)를 고려하여 적절한 샤드키를 결정하게 된다.
수직 파티셔닝(vertical partitioning)은 하나의 엔티티에 저장된 데이터들을 다수의 엔티티들로 분리하는것을 말한다. (마찬가지로 공간이나 퍼포먼스의 이유로) 예를 들면, 한 고객은 하나의 청구 주소를 가지고 있을 수 있다. 그러나 나는 데이터의 유연성을 위해 다른 데이터베이스로 정보를 이동하거나 보안의 이슈등을 이유로 CustomerId를 참조하도록 하고 청구 주소 정보를 다른 테이블로 분리할 수 있다.
요약하면 파티셔닝은 퍼포먼스, 가용성, 정비용이성등의 목적을 위해 논리적인 엔티티들을 다른 물리적인 엔티티들로 나누는것을 의미하는 일반적인 용어이다. 수평 파티셔닝 또는 샤딩은 스키마 복제 후 샤드키를 기준으로 데이터를 나누는것을 말한다. 수직 파티셔닝은 스키마를 나누고 데이터가 따라 옮겨가는것을 말한다.

References

profile
기록을 통한 성장을

3개의 댓글

comment-user-thumbnail
2023년 8월 30일

안녕하세요 가상 면접 사례로 배우는 대규모 시스템 설계 기초를 읽으면서 내용 정리할 때 참고하기 위해 블로그를 읽는 중에 문의사항이 생겨서 댓글 남깁니다.
다름이 아니라 데이터베이스 다중화 단계에서 마스터-슬레이브구조 데이터베이스에 대한 예시를
selectALL 은 slaveDB
selectById는 master DB
update master DB
라고 적어주셨는데, 읽기연산같은 경우는 슬레이브에서 되어야 하는 걸로 아는데 왜 selectById는 마스터에서 하는지 궁금해서 댓글 남깁니다.

1개의 답글
comment-user-thumbnail
2024년 1월 11일

안녕하세요. 책과 관련된 내용을 포스팅한 블로그 중에 가장 정리가 잘된 글 같습니다. 감사합니다.

답글 달기