가상면접사례로 배우는 대규모 시스템 설계 기초
오늘 배운 것
1장 사용자 수에 따른 규모 확장성
단일서버
데이터베이스
관계형, 비 관계형 = NoSQL이 있다.
어떤 DB를 사용할 것인가?
- 관계형
자료를 테이블과 열, 칼럼으로 구분한다. SQL을 사용하면 여러 테이블에 있는 데이터를 관계에 따라
join할 수 있다. - 비관계형
다시 4개 부류로 나누면
키-밸류 store 그래프 store 칼럼 store 문서 store
로 나뉜다.
관계형 DB에 대해 다음과 같은 이점이 있다.
- 아주 낮은 응답 지연시간(Latency)가 요구됨 예를들어, MySQL은 데이터를 Disk에 저장하고, 버퍼 풀 같은게 있긴 하지만 dist 들리긴 한다. 반면 Redis는 메모리에서만 돌아가므로 훨씬 빠를것이다.
- 다루는 데이터가 비정형이라 관계형 데이터가 아님 MySQL은 스키마를 정해두고 정형데이터만 사용하지만, 새로운 데이터 칼럼이 추가되어 스키마 변경시, 대용량 테이블 기준으로 큰일난다. 인덱스가 무효화되고 테이블 풀스캔 및 기존 서비스 중단…
- 데이터(JSON, YAML, XML)를 직렬화 하거나, 역직렬화 할 수 있기만 하면 됨 JPA 같은 ORM이 겪는 자바 객체와 테이블의 미스매칭이 아예 없는 NoSQL (MongoDB)
- 아주 많은 양의 데이터를 저장할 필요가 있음 수평적 확장을 고려해야 할 때, MySQL은 샤딩을 해야 하는데, 이게 원래 제공하는 기능이 아니라 애플리케이션 단에서 하는거임. 하지만 카산드라 ,HBase같은 Col-family store, MongoDB는 설계 단계부터 수평확장을 염두에 두었다.
수직적 규모확장 vs 수평적 규모확장
스케일 업 - 수직적 규모 확장, 서버의 부품 추가. 스케일 아웃 - 수평적 규모확장, 더 많은 서버
트래픽 양이 적다면 스케일 업이 유효하다. 가장 큰 장점은 단순함이다.
스케일 업의 단점
- 한 대의 서버에 CPU, mem을 무한히 붙일수는 없다.
- 장애에 대한 자동복구(failover)방안이나 다중화(redundancy)방안을 제공하지 않는다. 장애 발생시 서비스는 완전 중단된다.
로드밸런서
부하 분산 집합(load balancing set)에 속한 웹 서버들에게 트래픽을 고르게 분산한다.
사용자는 로드밸런서의 공개 IP주소에 접속한다. 서버간 통신에는 Private IP로 서로 통신한다.
로드밸런서를 추가하면 자동복구 = Failover가 가능해진다. 그리고 웹 계층의 가용성이 향상된다.
문제 발생시 부하 분산 집합에 서버를 추가하기만 하면 된다.
이제 웹계층은 해결, DB 는 여전히 하나인데 어떻게 할까.
데이터베이스 다중화
많은 DBMS가 다중화를 지원하며, 보통 주-부 관계를 두고 원본은 주, 사본을 부에 저장한다.
write, update는 주에만 하고 read는 부에다가 한다.
대부분의 애플리케이션은 read 비율이 2:8, 1:9 정도로 높다.
다중화의 이득은 다음과 같다.
- 더 나은 성능 : 연산이 분산되므로, 병렬로 처리될 수 있는 query의 수가 늘어나므로 성능이 좋아진다.
- 안정성(realiability) : DB 일부가 파괴되어도 데이터가 보존된다. 지역적으로 떨어진 여러 장소에 다중화 할 수 있기 때문.
- 가용성(availability) : 데이터를 여러 지역에 복제해두어서, 장애 발생 시 다른 서버 DB 로 서비스 지속 가능
앞절에서 로드밸런서는, 웹계층 부하에 대해 서버 추가만 하면 해결 가능, DB도 그럴까?
- 부 가 한 대 밖에 없는데 다운되었다면, 읽기 연산은 master로 전달된다. 또 즉시 새로운 slave가 장애를 대체한다. slave가 여럿이라면 다른 slave가 대체한다.
- master가 다운된다면, slave가 하나라면 그게 새로운 master가 된다. 모든 query는 master에서 수행된다. 그리고 새로운 slave가 추가된다. 프로덕션 환경에선 사실 더 복잡한데, slave에 보관된 데이터가 최신이 아닐 수 있다. 없는 데이터는 복구 스크립트를 돌려서 추가해야 한다. multi master나 원형 다중화를 도입하면 대처에 도움이 되지만, 책의 범위를 벗어난다.
RDB의 다중화 vs newSQL
- 다중 master의 경우, 비동기 update가 되면, 서로 충돌을 일으킬 수가 있다. *(write conflict) 이것을 해결하기 위한 여러 방법도 있긴 한데. 다른 얘기
-cockRoachDB 같은 newSQL들이 있다. 모든 노드가 read, write를 수행하도록 한다. Raft 합의 프로토콜을 통해, 과반수 노드의 동의가 있어야 트랜잭션이 커밋된다. 물론, 동의받는 과정에서 시간지연이 발생하긴 한다. ---
이제 웹계층(로드밸런서)과 DB(다중화)로 부하를 분산할 수 있게 되었다.
응답 시간의 경우, 캐시를 붙이고, 정적 콘텐츠를 CDN으로 옮겨 개선할 수 있다.
캐시
값비싼 연산결과, 자주 참조되는 데이터를 메모리 안에 두고, 뒤이은 요청이 보다 빨리 처리될 수 있도록 하는 저장소다.
DB I/O가 비용이 비싸므로, 웹 서버 입장에서는 이것을 줄이는게 관건인데, 캐시는 이 문제를 완화할 수 있다.
캐시 계층
별도의 캐시 계층을 두면, 성능이 개선될 뿐만 아니라, 데이터베이스의 부하도 줄일 수 있고, 캐시 계층의 규모를 독립적으로 확장시키는 것도 가능하다.
캐시 전략의 대표적인걸로
읽기 주도형 캐시 전략(Read-Through Caching Strategy) 가 있다.
요청받은 웹서버는 캐시에 응답이 저장되어 있는지를 본다. 있으면 가져오고 없으면 DB가서 읽어 캐시에 쓴다.
데이터의 종류, 크기, 엑세스 패턴에 맞는 캐시전략들이 존재한다.