사용자가 늘면 서버 하나로는 충분하지 않아서 여러 서버를 두어야 한다. 하나는 웹/모바일 트래픽 처리 용도고, 다른 하나는 데이터베이스용이다.
데이터베이스는 크게 RDBMS, NoSQL 2가지로 나눌 수 있다. 대체적으로는 RDBMS로 해결할 수 있고 적절한 상황이지만, 아래와 같은 경우에는 NoSQL이 적절한 선택일 수 있다.
🤔 Graph Database(Neo4J)
데이터가 서로 복잡하게 연결되어 있고, 그 데이터가 다른 데이터와 어떻게 연결되고 참조하는지를 표현해야 할때 적용하면 효과적이다. 전통적인 RDBMS에서는 다대다의 관계이 경우를 말한다. 실시간 추천엔진, 콘텐츠 및 자산관리 시스템, ID 및 엑세스 관리 시스템, 규정 준수 및 위험관리 솔루션, 소셜네트워크와 같은 관계를 보고자 할때 사용한다.
수많은 조인을 줄여줌
서버로 유입되는 트래픽의 양이 적을 때는 수직적 확장이 좋은 선택이며, 이 방법의 가장 큰 장점은 단순함이다. 그러나 스케일 업에는 심각한 단점이 존재한다.
위와 같은 단점 때문에, 대규모 애플리케이션을 지원하는 데는 수평적 규모 확장법이 보다 적절하다.
사용자는 로드밸런서의 Public IP
로 접속한다. 서버 간 통신에는 Private IP
가 이용된다. 위처럼 스케일 아웃을 통해서 서버를 여러 대로 늘리게 되면 no failover도 해소할 수 있고, 웹 계층의 가용성은 향상된다.
Elastic Load Balancing의 작동 방식
로드 밸런서는 클라이언트에서 오는 트래픽을 허용하고, 하나 이상의 가용 영역에서 등록된 대상(예: EC2 인스턴스)으로 요청을 라우팅합니다. 또한, 로드 밸런서는 등록된 대상의 상태를 모니터링하고 정상 대상으로만 트래픽이 라우팅되도록 합니다. 로드 밸런서가 비정상 대상을 감지하면, 해당 대상으로 트래픽 라우팅을 중단합니다. 그런 다음 대상이 다시 정상으로 감지되면 트래픽을 해당 대상으로 다시 라우팅합니다.
하나 이상의 리스너를 지정하여 들어오는 트래픽을 허용하도록 로드 밸런서를 구성합니다. 리스너는 연결 요청을 확인하는 프로세스입니다. 클라이언트와 로드 밸런서 간의 연결을 위한 프로토콜 및 포트 번호로 구성됩니다. 마찬가지로 로드 밸런서와 대상 간의 연결을 위한 프로토콜 및 포트 번호로 구성됩니다.
데이터베이스를 Master-Slave
로 나누는데 쓰기 연산
은 Master
에서만 지원하고, 읽기 연산
은 Slave
에서만 지원한다. 대부분의 애플리케이션에서는 쓰기 작업
보다는 읽기 작업
이 훨씬 많기 때문에 Slave 데이터베이스가 훨씬 많다.
select
은 slaveDBupdate
master DB캐시는 값 비싼 연산 결과 또는 자주 참조되는 데이터를 메모리 안에 두고, 뒤이은 요청이 보다 빨리 처리될 수 있도록 하는 저장소이다. 애플리케이션의 성능은 데이터베이스를 얼마나 자주 호출하느냐에 크게 좌우되는데, 캐시는 그런 문제를 완화할 수 있다.
캐시 계층은 데이터가 잠시 보관되는 곳으로 데이터베이스보다 훨씬 빠르다.
CDN은 정적 콘텐츠를 전송하는 데 쓰이는, 지리적으로 분산된 서버의 네트워크이다. 이미지, 비디오, CSS, JavaScript 파일 등을 캐시할 수 있다.
이제는 웹 계층을 수평적으로 확장하는 방법을 고민해보자.
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를 참조하도록 하고 청구 주소 정보를 다른 테이블로 분리할 수 있다.
요약하면 파티셔닝은 퍼포먼스, 가용성, 정비용이성등의 목적을 위해 논리적인 엔티티들을 다른 물리적인 엔티티들로 나누는것을 의미하는 일반적인 용어이다. 수평 파티셔닝 또는 샤딩은 스키마 복제 후 샤드키를 기준으로 데이터를 나누는것을 말한다. 수직 파티셔닝은 스키마를 나누고 데이터가 따라 옮겨가는것을 말한다.
안녕하세요 가상 면접 사례로 배우는 대규모 시스템 설계 기초를 읽으면서 내용 정리할 때 참고하기 위해 블로그를 읽는 중에 문의사항이 생겨서 댓글 남깁니다.
다름이 아니라 데이터베이스 다중화 단계에서 마스터-슬레이브구조 데이터베이스에 대한 예시를
selectALL 은 slaveDB
selectById는 master DB
update master DB
라고 적어주셨는데, 읽기연산같은 경우는 슬레이브에서 되어야 하는 걸로 아는데 왜 selectById는 마스터에서 하는지 궁금해서 댓글 남깁니다.