DB - MongoDB 샤딩(Sharding,분산처리 등)

2019. 9. 12. 23:26Database/MongoDB

 

샤딩(Sharding)은 데이터를 여러 서버에 분산해서 저장하고 처리할 수 있도록 하는 기술을 말한다. MongoDB의 복제와 샤딩은 상호보완적이지만 엄연히 목적이 다르다. 복제는 고가용성을 위한 솔루션이며 샤딩은 분산 처리를 위한 솔루션이다. 그래서 몽고디비는 고가용성과 대용량 분산 처리를 위해 복제와 샤딩을 모두 적용한다.

 

몽고디비에서 샤딩을 적용하려면 샤드 클러스터를 구축해야 하는데, 이를 위해서는 파티션된 데이터의 범위와 샤드 위치 정보 등의 메타 정보를 저장하기 위해 컨피그 서버(Config Server)가 필요하다. 그리고 응용 프로그램이 필요한 데이터를 조회하거나 저장하려면 "mongos"라는 라우터 서버(Router Server)가 필요하다. 라우터는 쿼리 수행에 있어서 프록시(Proxy) 역할을 하는데, 컨피그 서버의 메타 정보를 참조하여 필요한 데이터가 저장된 샤드로 쿼리를 전달하고 그 결과를 다시 응용 프로그램으로 반환하는 역할을 한다. 그래서 우리는 몽고디비 클라이언트 드라이버를 사용하여 몽고디비를 이용할 때, 각 샤드의 메타 정보에 대해서 신경쓸 필요가 없다. 사용자가 필요로 하는 데이터에 접근하는 것을 보이지 않는 부분에서 투명하게 처리된다.

 

그렇다면 스케일업(Scale-up)과 스케일아웃(Scale-out)인 샤딩과의 차이는 무엇일까?

 

 

위 그림은 스케일업과 스케일아웃의 성능 그래프이다. 서버의 스펙을 점차 키우면 특정 구간에는 성능이 향상되지만 어느 구간 이상이 되면 성능이 증가하지 못한다. 하지만 스케일아웃은 값싼 비용으로 여러대의 서버를 점점 늘리게되면 그만큼 성능이 비례해서 증가하게 된다. 물론 상황에 따라 다를 수 있다. 샤딩은 이러한 스케일아웃구조를 위한 분산처리 기술인 것이다.

 

MongoDB 샤딩 아키텍쳐

 

 

몽고디비 샤드 클러스터의 가장 중요한 3가지 컴포넌트는 샤드서버와 컨피그서버 그리고 라우터다. 하나의 샤드 클러스터에 샤드 서버는 레플리카 셋 형태로 1개 이상 존재할 수 있으며, 라우터 서버도 1개 이상 존재할 수 있다. 하지만 컨피그서버는 하나의 샤드 클러스터에 단 하나의 레플리카 셋만 존재할 수 있다.

 

샤드 클러스터의 3가지 컴포넌트 중에서 라우터(mongos)는 영구적인 데이터를 가지지 않으며, 사용자의 쿼리 요청을 어떤 샤드로 전달할지 정하고, 각 샤드로부터 받은 쿼리 결과 데이터를 병합해서 사용자에게 되돌려주는 역할을 한다. 하지만 샤드 서버와 컨피그서버는 영구적인 데이터를 저장하는데, 샤드 서버는 실제 사용자의 데이터를 저장하는 반면 컨피그서버는 샤드 서버에 저장된 사용자 데이터가 어떻게 스플릿 되어서 분산돼 있는지에 관한 메타 정보를 저장한다. 사용자의 데이터이든 아니든 관계없이 샤드서버와 컨피그서버의 데이터는 손실될 경우 치명적인 문제가 발생할 수 있기에 레플리카 셋으로 구축할 것을 적극 권장한다.

 

라우터(mongos)서버는 사용자의 요청을 각 샤드로 전송하고 결과를 사용자에게 전달하는 역할도 수행하지만, 각 샤드가 균등하게 데이터를 가지고 있는지 모니터링하면서 데이터의 밸런싱 작업도 담당한다. 그림을 보면 상당히 복잡하지만, 실제 컨피그 서버와 샤드서버는 모두 동일하게 작동한다. 즉 컨피그 서버와 샤드서버는 모두 "mongod"라는 프로그램으로 실행되며, 단 한가지 차이는 컨피그 서버와 샤드서버의 설정이 조금씩 다르다는 것이다.

 

샤드 클러스터의 쿼리 수행 절차(라우터와 컨피그 서버 통신)

컨피그 서버는 샤드 클러스터에서 사용자가 생성한 데이터베이스와 컬렉션들의 목록 등 많은 정보를 관리한다. 하지만 모든 데이터베이스와 컬렉션의 목록을 관리하는 것이 아니라, 샤딩이 활성화된 데이터베이스와 컬렉션의 정보만 관리한다. 실제 샤드 클러스터에 데이터베이스나 컬렉션을 생성해도 샤딩이 되지 않은 객체들은 컨피그 서버가 아니라 각 샤드서버가 로컬로 관리한다. 그리고 각 컬렉션이 여러 샤드 서버로 분산될 수 있게 분산하기 위한 정보를 관리하는데, 이렇게 하나의 큰 컬렉션을 여러 조각으로 파티션하고 각 조각을 여러 샤드 서버에 분산해서 저장한다. 이때 이런 각 데이터 조각을 몽고디비에서는 청크(Chunk)라고 한다.

 

 

사용자가 쿼리 요청을 보내면 아래와 같이 클러스터는 동작한다.

 

  • 1.사용자 쿼리가 참조하는 컬렉션의 청크 메타 정보를 컨피그 서버로부터 가져와서 라우터의 메모리에 캐시한다.
  • 2.사용자 쿼리의 조건에서 샤딩 키 조건을 찾는다.
  • 2-A. 쿼리 조건에 샤딩 키가 있으면 해당 샤딩 키가 포함된 청크 정보를 라우터의 캐시에서 검색하여 해당 샤드 서버로만 사용자 쿼리를 요청한다. 샤딩 키 조건에 포함된 청크가 여러 샤드에 분산되있다면 대상이 되는 여러 샤드에 쿼리를 요청한다.
  • 2-B.쿼리 조건에 샤딩 키가 없으면 모든 샤드로 쿼리 요청한다.
  • 3.쿼리를 전송한 대상 샤드 서버로부터 쿼리 결과가 도착하면 결과를 병합하여 사용자에게 쿼리 결과로 커서를 반환한다.

라우터가 쿼리를 실행하는 절차에서 1번 과정은 라우터가 청크 메타 정보를 가지고 있지 않거나, 라우터가 가진 청크 메타 정보가 오래돼서 맞지 않을 때만 수행된다. 즉, 각 쿼리요청에 매번 컨피그 서버에 요청을 보내는 것이 아니다.

 

컨피그 서버

컨피그 서버는 샤딩된 클러스터를 운영하는 데 있어서 필요한 모든 정보를 저장한다. 수많은 청크에 대한 메타 정보를 가지고 있기에 사용자 데이터의 일관성을 유지하기 위한 매우 중요한 정보이므로 몽고디비는 컨피그 서버를 반드시 3대 이상으로 복제할 것을 권장한다.

 

복제방법으로는 미러링방식과 레플리카셋 방식이 있는데, 레플리카셋을 주로 사용한다. 미러링 방식의 복제는 클라이언트 프로그램의 메타 데이터 쿼리나 데이터 변경 절차를 복잡하게 만들면서 문제를 유발할 가능성이 높다.

 

레플리카 셋으로 컨피그 서버를 구축하는 경우에는 다음과 같은 몇 가지 조건을 만족해야한다.

  1. 컨피그 서버는 반드시 WiredTiger 스토리지 엔진을 사용해야 한다.
  2. 레플리카 셋은 아비터를 가질 수 없다.
  3. 레플리카 셋은 지연된 멤버를 가질 수 없다.
  4. 최소 3개 이상의 멤버로 구성해야 한다.

몽고디비 3.0까지는 모든 컨피그 서버가 다운되도 클러스터를 운영할 수 있었지만 여러가지 문제로 컨피그서버가 모두 다운되면 클러스터도 동일하게 운영되지 않도록 업데이트 됬다. 즉, 컨피그 서버도 가용 상태를 유지하는 것이 좋다.

 

라우터

라우터 서버는 사용자의 쿼리 요청을 샤드 서버로 전달하고, 샤드 서버로부터 쿼리 결과를 모아서 다시 사용자에게 반환하는 프록시 역할을 수행한다.

 

라우터 서버의 중요 역할은 아래와 같다.

  1. 사용자 쿼리를 전달해야 할 샤드 서버를 결정하고 해당 샤드로 쿼리 전송
  2. 샤드 서버로부터 반환된 결과를 조합하여 사용자에게 결과 반환
  3. 샤드간 청크 밸런싱 및 청크 스플릿 수행

라우터는 컨피그 서버로부터 클러스터의 메타 정보를 메모리에 캐시하고 있어서 사용자 쿼리를 받으면 그 쿼리를 어느 샤드 서버로 전달해야 할지 판단할 수 있다. 그리고 라우터 서버는 샤드 서버로부터 받은 결과를 병합하여 사용자에게 반환하는 작업을 수행하는데, 이 과정에서 라우터는 샤드 서버가 내려준 결과를 단순히 모아서 사용자에게 반환하기만 하는 것이 아니다. 라우터 서버는 각 샤드가 내려준 결과가 실제 그 샤드 서버가 가지지 말아야할 데이터인지 판단하고(리밸런싱,청크 스플릿,청크 마이그레이션 과정중 다른 샤드에 잘못 들어간 데이터 등) 그 데이터를 제거하는 작업도 수행한다. 또한 샤드 서버와 협력하여 데이터 정렬,LIMIT,SKIP등의 기능을 제공해준다.

 

라우터의 쿼리 분산

몽고디비 샤드 클러스터에서 컬렉션은 특정 필드의 값을 기준으로 샤딩될 수 있는데, 사용자의 쿼리가 이 샤딩 기준 키 값에 대한 조건을 가지고 있느냐에 따라 라우터가 쿼리를 요청해야 할 샤드 서버를 결정하게 된다. 라우터가 사용자의 쿼리를 특정 샤드로만 요청하는 형태를 타겟쿼리(Target Query)라고 하며, 모든 샤드 서버로 요청하는 경우를 브로드캐스트 쿼리(Broadcast Query)라고 한다.

 

<타겟쿼리>

타켓쿼리는 앞에서 설명한 것과 같이 문서의 필드 값(샤딩의 기준 필드)을 기준으로 쿼리를 보낼 샤딩을 결정한다. 만약 기준 필드가 2개여도 타켓 쿼리가 가능하다. 아래와 같은 쿼리가 있다고 가정하자.

 

샤드 기준 필드(date,time)

mongos > db.coll.find({date:"2015-01-01",time:{$gte:"00:00:00",$lt:"13:00:00"}}) -> 타켓쿼리 적용

mongos > db.coll.find({date:"2015-01-01"}}) -> 타켓쿼리 적용(기준필드가 2개일때, 앞에있는 date조건만 걸려도 샤드 키의 일부로 특정 샤드 제한 가능, 반드시 선행 필드가 들어와야함)

mongos > db.coll.find({time:{$gte:"00:00:00",$lt:"13:00:00"}}) -> 타켓쿼리 적용안됨. 선행 필드가 들어올때만 특정 샤드를 제한할 수 있다.

 

이러한 타켓 쿼리는 INSERT,UPDATE,DELETE 쿼리 모두에서 가능하다.

 

<브로드캐스트 쿼리>

샤드 클러스터 몽고디비에서 샤드 키를 쿼리 조건으로 가지지 않은 경우에는 라우터가 작업 범위를 특정 샤드로 줄일 수 없다. 그래서 이런 경우 라우터는 사용자의 쿼리를 모든 샤드로 요청하고 각 샤드로부터 온 결과를 병합해 사용자에게 반환해준다.

 

라우터 배포

배포 방식 설명
응용 프로그램 서버(App Server)와 함께 라우터 배포

응용 프로그램 서버에 몽고디비 라우터를 같이 실행하는 방법으로 몽고디비 메뉴얼에서 권장하는 가장 일반적인 형태의 배포 방식이다. 이 형태의 배포에서 가장 중요한 것은 각 응용 프로그램 서버에서 실행 중인 라우터는 로컬 서버에서 실행 중인 응용 프로그램 서버로부터의 연결만 처리한다는 것이다.(즉, 같은 응용 프로그램이 3개 삼중화 되어 있다면 각 응용 프로그램 서버에 라우터가 하나씩 배포되어 실행) 각 응용 프로그램에 포함된 몽고디비 클라이언트 드라이버는 원격에 실행 중인 몽고디비 라우터로는 연결되지 않고, 로컬에 설치된 라우터랑만 통신한다.

 

이 경우 몽고디비 드라이버의 ConnectionString에는 로컬의 라우터로만 연결하도록 "127.0.0.1:27017"만 명시한다.

 

구성하기 쉬운 장점이 있지만, 문제점으로는 라우터 서버가 하나 다운되면 해당 서버의 응용 프로그램도 문제가 없더라도 다운시켜야하는 문제가 발생한다. 또한 라우터가 많아질 수록 샤드 서버에 붙는 커넥션수가 많아져 부하를 일으킬 수 있다.

전용 라우터 서버에 여러 라우터 서버 배포

몽고디비 라우터를 전용의 서버에서 실행하고, 응용 프로그램 서버는 하나 이상의 몽고디비 라우터 서버로 접속하여 쿼리를 실행하는 방법을 고려한 배포이다.

이런 배포 형태에서는 응용 프로그램 서버의 수만큼의 라우터가 실행될 필요가 없으므로 라우터의 서버 수가 줄고 그로인해 샤드서버의 커넥션 수도 줄어든다.

 

이 경우 몽고디비 드라이버의 ConnectionString에 1개 이상의 라우터 주소를 명시한다.

 

공식사이트에서는 이 방법을 권장하지 않는다.하나의 라우터서버에 부하가 집중될 수도 있는 등 여러가지 신경써야할 문제가 있기 때문이다.

L4와 함께 라우터 배포 여러 라우터 서버가 떠있고 그 앞단에 L4 장비를 두는 방식이다. 
샤드 서버나 컨피그 서버에 라우터 서버를 함께 배포 라우터는 컴퓨팅 자원을 많이 사용하지 않아 샤드 서버에 같이 라우터 서버를 배포하기도 한다.

 

여기까지 간단하게 몽고디비 샤딩에 관련된 내용을 다루어봤다. 마지막의 라우터 배포형식은 추후에 더 자세히 포스팅할 예정이다.