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

 

샤딩(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 장비를 두는 방식이다. 
샤드 서버나 컨피그 서버에 라우터 서버를 함께 배포 라우터는 컴퓨팅 자원을 많이 사용하지 않아 샤드 서버에 같이 라우터 서버를 배포하기도 한다.

 

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

posted by 여성게
:
Database/MongoDB 2019. 9. 12. 19:16

 

몽고디비(MongoDB)에서는 마스터-슬레이브 복제와 레플리카 셋 복제라고 하는 두 가지 방식의 복제를 지원한다. 마스터-슬레이브 복제는 몽고디비가 만들어졌던 초기에 사용하던 복제 방식으로, 몽고디비3.2 버전에서는 권장하지 않는 방식이다. 또한 마스터-슬레이브 복제 방식은 마스터의 장애에 대한 페일오버를 관리자가 수동으로 처리해야 하며, 최근 버전의 몽고디비에서는 거의 기능이 개선되거나 보완되지 않고 있다. 그에 반해서 레플리카 셋 복제는 안정되고 많은 부분 자동화되어 처리될 수 있게 개발됐다.

 

복제란?

복제는 여러 서버가 서로의 데이터를 동기화하는 것을 의미하는데, 서로 주고받는 데이터에 따라서 논리 복제와 물리 복제로 나눌 수 있다. DRBD(Distributed Replicated Block Device)와 같이 리눅스 서버가 데이터의 내부를 전혀 모르는 상태에서 디스크의 블록만 복제하는 형태를 물리적 복제라고 한다. 그리고 MySQL 서버나 MongoDB 서버와 같이 데이터베이스 서버가 직접 서버간의 데이터를 동기화하는 방식을 논리적 복제라고 한다. 물리적 복제는 데이터베이스가 전혀 관여하지 않으므로 운영체제 차원에서 응용 프로그램에 투명하게 복제를 처리할 수 있지만, 응용 프로그램의 캐시나 내부 처리 로직에서 변경된 데이터를 사용하지 못하는 문제점도 있다. 

 

몽고디비는 MySQL 서버의 복제와 아주 비슷하게 논리적 복제 수단을 가지고 있으며, 더불어 프라이머리 노드의 선출과 네트워크 단절로 인한 스플릿 브레인(Split-Brain) 현상을 막을 수 있는 기능까지 내장하고 있다.

 

컨센서스 알고리즘(Consensus Algorithm)

여러 서버가 복제에 참여해서 서로 같은 데이터를 동기화하는데, 이렇게 데이터를 공유하는 그룹을 레플리카 셋이라고 한다. 그리고 하나의 레플리카 셋에는 프라이머리와 세컨드리 노드로 각자의 역할이 나뉜다. 레플리카 셋이 참여하는 각 멤버들은 각자의 역할에 맞게 작동하면서 서로의 데이터를 동기화하고, 특정 노드가 응답 불능이 됬을 때 어떻게 대처할 것인지 등을 결정한다. 이때 어떻게 작동할지 결정하는 것을 켄센서스 알고리즘이라고 하는데, 몽고디비는 확장된 형태의 Raft 컨센서스 모델을 사용한다.

 

켄센서스 알고리즘은 크게 Paxos와 Raft 컨센서스 알고리즘이 있다.

 

Raft 켄센서스 알고리즘의 가장 큰 특징은 리더 기반의 복제와 각 멤버 노드가 상태를 가진다는 것이다. 하나의 레플리카 셋에는 반드시 하나의 리더만 존재할 수 있고, 리더는 사용자의 모든 데이터 변경 요청을 처리한다. 그리고 리더는 사용자의 요청 내용(데이터 변경)을 로그에 기록하고, 모든 팔로워는 리더의 로그를 가져와서 동기화를 수행한다. Raft 켄센서스 알고리즘의 리더를 몽고디비에서는 프라이머리 노드라고 하고 팔로워는 세컨드리 노드라고 한다. 그리고 로그를 몽고디비에서는 OpLog(Operation Log)라고 한다.

 

복제의 목적은?

복제의 가장 큰 목적은 동일한 데이터를 이중 삼중으로 유지함으로써 레플리카 셋의 특정 멤버에서 데이터 손실이 발생하더라도 다른 멤버의 데이터로 대체할 수 있도록 하기 위함이다. 즉 고가용성(High-Availability)을 위해서 중복된 데이터 셋을 준비하는 것이다.

 

고가용성을 위해서 몽고디비 레플리카 셋의 각 멤버는 서로 다른 멤버가 살아있는지 계속 확인하는 하트비트 메시지를 주고 받는다. 만약 프라이머리 노드와 통신되지 않으면 새로운 프라이머리 노드를 선출해 서비스가 계속 지속되도록 한다.

 

복제의 또 다른 목적으로는 데이터조회 쿼리의 로드 분산을 생각할 수 있다. 몽고디비에서 데이터의 변경은 프라이머리 노드에서 시작되지만, 각 세컨드리 노드는 데이터변경은 하지 못하지만 조회쿼리의 요청은 받을 수 있다. 즉, 읽기 성능을 높히기 위해 세컨드리 노드를 확장해서 사용하기도 한다.(노드를 늘림)

 

레플리카 셋에서 조금은 특이한 역할을 노드가 존재한다. 이 노드는 아비터노드라고 한다. 아비터노드는 데이터를 가지고 있지 않고 단순히 프라이머리 선출을 위한 투표에 참가할 뿐이다.(정족수==쿼럼을 채우기 위함) 그렇다면 이런 노드는 왜 사용하지라고 생각할 수 있지만 하나 상황을 가정해보자.

 

하나의 레플리카 셋은 반드시 과반수 이상의 멤버가 투표에 참여해서 승인을 얻어야 프라이머리 멤버를 선출할 수 있다. 즉 과반수 이상의 멤버가 살아있어야 사용자의 쓰기 요청을 처리할 수 있음을 의미한다. 만약 과반수 이상의 멤버가 응답 불가능한 상태에는 프라이머리를 선출할 수 없으며, 레플리카 셋의 모든 멤버가 세컨드리로 남게된다. 이런 급박한 상황에 레플리카 셋의 멤버를 투입하는 것은 상당히 많은 시간이 필요하다. 왜냐하면 새로운 노드는 레플리카 셋의 데이터를 가져야하기에 이러한 데이터 동기화를 위한 비용이 들기 때문이다. 만약 데이터가 작다면 상관없지만 아주 크다면 레플리카 셋에 참여하는 새로운 멤머가 데이터를 가지기 위해 리밸런스가 일어날 것이고 엄청난 시간이 소요될 것이다. 이런 경우 아비터 노드로 레플리카 셋에 멤버를 추가하면 데이터를 위한 비용은 거의 들이지 않고 빠르게 투표에 참여할 수 있게 되어 서비스 정상화를 위한 시간이 아주 빨라진다.

 

그래서 보통 몽고디비 고가용성을 보장하기 위해 레플리카 셋은 최소 3개로 구성하며 프라이머리노드,세컨드리노드,아비터노드 이렇게 3개의 노드로 레플리카 셋을 구성한다. 만약 프로덕 환경에 아비터를 사용하는 경우에는 반드시 아비터를 위한 전용 서버를 고려하는 것이 좋다. 아비터는 별도로 사용자의 데이터를 보관하거나 처리하지 않기 때문에 높은 사양의 하드웨어를 필요로 하지 않기 때문이다. 아니면 아비터 서버들을 위한 별도 서버를 마련하여 아비터 인스턴스 여러개를 전용 서버에서 구동시켜도 좋다.

 

이전 포스팅에서도 거론했지만 다시 한번 하나의 레플리카 셋에 1개 이상의 아비터 노드는 구성하지 말자.

 

레플리카 셋 노드 종류

  1. 프라이머리 노드
  2. 세컨드리 노드
  3. 아비터 노드

 

이번 포스팅에서는 내부적인 동작방식은 다루지 않는다. 나중에 기회가 된다면 리더 선출 및 기타 내부적인 구동 방식을 다루어 볼것이다.

posted by 여성게
:
Database/MongoDB 2019. 9. 12. 18:22

 

요즘 시대에는 구글이나 페이스북과 같은 글로벌 서비스를 제공하는 회사가 늘어나면서 방대한 양의 데이터를 충분히 빠른 속도로 처리할 수 있는 데이터베이스에 대한 필요성이 대두되기 시작했다. 이런 대용량 데이터 서비스에서는 기존의 RDBMS에서 처리하기는 힘들다.(비용적인 문제, 데이터를 분산하기 위해 수십,수백대의 서버로 분산시켜야함) 물론 MySQL 같이 오픈 소스 RDBMS는 비용적인 문제를 해결해주기는 하였다. 하지만 이러한 MySQL도 빅데이터를 처리하기에는 문제가 있다. 만약 엄청난 양의 데이터가 있고, 매번 데이터베이스 스키마에 맞게 데이터를 조작하여 작업을 해야한다면 얼마나 비효율적이고 힘든 작업일까..

 

여느 NoSQL과 같이 MongoDB는 이러한 문제를 해결하기 위해 적합한? 데이터베이스이다. 솔루션 자체적으로 분산 처리, 샤딩, 데이터 리밸런싱, 데이터 복제, 복구 등을 지원하고 무엇보다 Schema-Free(Schema-less)한 구조이기에 대용량의 데이터 작업에 아주 효율적인 데이터베이스이다. 또한 일부 RDBMS의 기능을 제공하기도 한다.(인덱싱 등, 내부적으로 B-Tree 자료구조를 이용하여 인덱스를 관리.)

 

 

What Is MongoDB?

MongoDB is a document database with the scalability and flexibility that you want with the querying and indexing that you need.

www.mongodb.com

공식 홈페이지에 나와있는 MongoDB의 소개이다.

 

  • MongoDB 는 유연하고 JSON과 유사한 문서에 데이터를 저장합니다 . 즉, 필드는 문서마다 다를 수 있으며 시간에 따라 데이터 구조를 변경할 수 있습니다.

  • 문서 모델 은 응용 프로그램 코드의 객체에 매핑 되므로 데이터를 쉽게 사용할 수 있습니다.

  • 임시 쿼리, 인덱싱 및 실시간 집계 는 데이터에 액세스하고 분석하는 강력한 방법을 제공합니다

  • MongoDB는 기본적 으로 분산 데이터베이스 이므로 고 가용성, 수평 확장 및 지리적 분포가 내장되어 있고 사용하기 쉽습니다.

  • MongoDB는 무료로 사용할 수 있습니다 . 

 

그렇다면 MongoDB와 기존의 RDBMS와의 차이점은 무엇일까?

 

데이터를 저장하는 자료 구조 관점에서 보면 MongoDB와 RDBMS는 많은 공통점이 있다. MongoDB에서는 컬렉션(Collection)이나 도큐먼트(Document)와 같이 객체의 이름이 조금 다를 뿐 RDBMS와 비슷한 역할을 한다.

 

MongoDB RDBMS
데이터베이스(Database) 데이터베이스(Database)
컬렉션(Collection) 테이블(Table)
도큐먼트(Document) 레코드(Record OR Row)
필드(Field) 컬럼(Column)
인덱스(Index) 인덱스(Index)
쿼리의 결과로 "커서(Cursor)" 반환 쿼리의 결과로 "레코드(Record)" 반환

 

MongoDB는 쿼리의 결과로 커서를 반환하는데, 응용 프로그램이나 MongoDB 클라이언트 프로그램에서 커서를 통해 반복적으로 실제 도큐먼트를 가져올 수 있다. MongoDB에서 쿼리의 결과로 커서를 반환하는 이유는 쿼리의 결과를 클라이언트 서버의 메모리에 모두 담아두지 않아도 처리할 수 있게 하기 위해서다. 물론 MongoDB에서 커서를 읽을 때마다 서버(MongoDB 서버)에서 그때그때 도큐먼트를 가져오는 것은 아니고, 필요할 때마다 지정된 페이지 사이즈 단위로 서버로부터 전송받아 MongoDB 클라이언트 서버에 캐싱한 후에 유저에게 서비스하는 것이다.

 

많은 사람이 이야기하는 MongoDB의 특성으로는 다음과 같은 것들이 있다.

  • NoSQL
  • 스키마 프리(Schema-Free)
  • 비 관계형 데이터베이스

MongoDB는 RDBMS의 SQL을 사용하지는 않지만  MongoDB는 SQL 못지 않은 다양한 종류의 쿼리문을 지원한다.(필터링, 수집, 정렬, 정규 표현식 등) 또한 MongoDB는 외래키를 명시적으로 지원하지는 않지만, 논리적으로 도큐먼트 간의 관계(Embedded Document)를 만들어서 사용하는 데에는 아무런 문제가 없다. 그리고 RDBMS와 같지는 않지만 "$lookup"이라는 집계 기능을 이용하면 관계형데이터베이스와 비슷한 형태의 조인 처리를 수행할 수 있다.(샤딩 환경에서는 여러 제약이 존재)

 

스키마 프리가 아마도 MongoDB&RDBMS를 구분 지어줄 수 있는 가장 좋은 단어가 이다. 여기에서 스키마 프리는 테이블의 컬럼 수준에만 적용되는데, 사용할 컬럼을 미리 정의하지 않고 언제든지 동적으로 필요한 시점에 데이터를 저장할 수 있다는 것을 의미한다.(이러한 의미에서 Elasticsearch와 Solr 같은 검색엔진(색인)도 일종의 NoSQL이라 불리지 않을 까 싶다. 실제 샤딩,복제 등의 기능도 대부분의 검색엔진 솔루션 자체적으로 지원해준다.) 하지만 MongoDB는 모든 부분에 있어서 스키마 프리라고는 보기 힘들다. 다른 NoSQL 데이터베이스와는 달리 보조 인덱스를 생성할 수 있는데, MongoDB의 보조 인덱스는 항상 먼저 인덱스를 구성하는 필드를 먼저 정의해야 한다.

 

이외의 MongoDB의 특징과 장단점들이다.

 

<특징>

 

  • Document-oriented storage : MongoDB는 database > collections > documents 구조로 document는 key-value형태의 BSON(Binary JSON)으로 되어있다.
  • Full Index Support : 다양한 인덱싱을 제공한다.
    • Single Field Indexes : 기본적인 인덱스 타입
    • Compound Indexes : RDBMS의 복합인덱스 같은 거
    • Multikey Indexes : Array에 미챙되는 값이 하나라도 있으면 인덱스에 추가하는 멀티키 인덱스
    • Geospatial Indexes and Queries : 위치기반 인덱스와 쿼리
    • Text Indexes : String에도 인덱싱이 가능
    • Hashed Index : Btree 인덱스가 아닌 Hash 타입의 인덱스도 사용 가능
  • Replication& High Availability : 간단한 설정만으로도 데이터 복제를 지원. 가용성 향상.
  • Auto-Sharding : MongoDB는 처음부터 자동으로 데이터를 분산하여 저장하며, 하나의 컬렉션처럼 사용할 수 있게 해준다. 수평적 확장 가능
  • Querying(documented-based query) : 다양한 종류의 쿼리문 지원. (필터링, 수집, 정렬, 정규표현식 등)
  • Fast In-Pace Updates : 고성능의 atomic operation을 지원
  • Map/Reduce : 맵리듀스를 지원.(map과 reduce 함수의 조합을 통해 분산/병렬 시스템 운용 지원, 하둡처럼 MR전용시스템에 비해서는 성능이 떨어진다)
  • GridFS : 분산파일 저장을 MongoDB가 자동으로 해준다. 실제 파일이 어디에 저장되어 있는지 신경 쓸 필요가 없고 복구도 자동이다.
  • Commercial Support : 10gen에서 관리하는 오픈소스

<장점>

  • Flexibility : Schema-less라서 어떤 형태의 데이터라도 저장할 수 있다.
  • Performance : Read & Write 성능이 뛰어나다. 캐싱이나 많은 트래픽을 감당할 때 써도 좋다.
  • Scalability : 애초부터 스케일아웃 구조를 채택해서 쉽게 운용가능하다. Auto sharding 지원
  • Deep Query ability : 문서지향적 Query Language 를 사용하여 SQL 만큼 강력한 Query 성능을 제공한다.
  • Conversion / Mapping : JSON형태로 저장이 가능해서 직관적이고 개발이 편리하다.

<단점>

 

  • JOIN이 없다. join이 필요없도록 데이터 구조화 필요
  • memory mapped file으로 파일 엔진 DB이다. 메모리 관리를 OS에게 위임한다. 메모리에 의존적, 메모리 크기가 성능을 좌우한다. 2-4를 참고하자.
  • SQL을 완전히 이전할 수는 없다.
  • B트리 인덱스를 사용하여 인덱스를 생성하는데, B트리는 크기가 커질수록 새로운 데이터를 입력하거나 삭제할 때 성능이 저하된다. 이런 B트리의 특성 때문에 데이터를 넣어두면 변하지않고 정보를 조회하는 데에 적합하다.

 

MongoDB의 Physical 데이터 저장구조

MongoDB를 구성할 때 보면, 가장 많이 이슈되는 부분 중 하나가 메모리량과 디스크 성능이다. 메모리 크기가 아주 민감한 요인이 된다. 

 

MongoDB는 기본적으로 memory mapped file(OS에서 제공되는 mmap을 사용)을 사용한다. 데이터를 쓰기할때, 디스크에 바로 쓰기작업을 하는 것이 아니라 논리적으로 memory 공간에 쓰기를 하고, 일정 주기에 따라서, 이 메모리 block들을 주기적으로 디스크에 쓰기한다. 이 디스크 쓰기 작업은 OS에 의해서 이루어 진다.

 

OS에 의해서 제공되는 가상 메모리를 사용하게 되는데, 물리 메모리 양이 작더라도 가상 메모리는 훨씬 큰 공간을 가질 수 있다. 가상 메모리는 페이지(Page)라는 블럭 단위로 나뉘어 지고, 이 블럭들은 디스크 블럭에 매핑되고, 이 블럭들의 집합이 하나의 데이터 파일이 된다.

 

 

메모리에 저장되는 내용은 실제 데이터 블록과, 인덱스 자체가 저장된다. MongoDB에서 인덱스를 남용하지 말라는 이야기가 있는데, 이는 인덱스를 생성 및 업데이트 하는데 자원이 들어갈 뿐더러, 인덱스가 메모리에 상주하고 있어야 제대로 된 성능을 낼 수 있기 때문이기도 하다.

 

만약에 물리 메모리에 해당 데이터 블록이 없다면, 페이지 폴트가 발생하게 되고, 디스크에서 그 데이터 블록을 로드하게 된다. 물론 그 데이터 블록을 로드하기 위해서는 다른 데이터 블록을 디스크에 써야한다.

 

즉, 페이지 폴트가 발생하면, 페이지를 메모리와 디스카 사이에 스위칭하는 현상이 일어나기 때문에 디스크IO가 발생하고 성능 저하를 유발하게 된다.

 

즉 메모리 용량을 최대한 크게 해서 이 페이지폴트를 예방하라는 이야기이다. 그러나, 페이지 폴트가 아예 발생 안할 수는 없다.(1TB의 데이터를 위해 메모리를 진짜 1TB만큼 올릴 수는 없다.) 그래서 페이지 폴트를 줄이는 전략으로 접근 하는 것이 옳은 방법이다.

 

페이지 폴트시 디스크로 write되는 데이터는 LRU 로직에 의해서 결정된다. 그래서, 자주 안쓰는 데이터가 disk로 out되는데, 일반적인 애플리케이션에서 자주 쓰는 데이터의 비율은 그리 크지 않다. 이렇게 자주 액세스되는 데이터를 Hot Data라고 하는데, 이 데이터들이 집중되서 메모리에 올라가도록 Key 설계를 하는 것이 핵심이다. 전체 데이터를 scan하는 등의 작업을 하게 되면, 무조건 페이지 폴트가 발생하기에 table scan이 필요한 시나리오는 별도의 index table(summary table)을 만들어서 사용하는 등의 전략이 필요하다.

 

MongoDB 배포 형태별 아키텍쳐

 

1)단일 노드

단일 노드로 MongoDB를 사용할 때에는 아무런 관리용 컴포넌트도 필요하지 않다. 클라이언트는 MongoDB 클라이언트 드라이버와 통신하며 MongoDB 클라이언트 드라이버는 1:1로 MongoDB 서버와 통신한다.

 

2)단일 레플리카 셋

레플리카 셋은 특정 서버에 장애가 발생했을 때, 자동 복구를 위한 최소 단위이므로 자동 복구가 필요하다면 항상 레플리카 셋으로 배포해야 한다. MongoDB 클라이언트 드라이버는 직접 MongoDB 서버로 접속하지만, 단일 노드로 접속할 때와 달리 레플리카 셋 옵션을 사용해야 한다. 하나의 레플리카 셋에는 항상 하나의 프라이머리 노드와 1개 이상의 세컨드리 노드로 구성되며, 프라이머리 노드는 사용자의 데이터 변경 요청을 받아서 처리하고, 세컨드리 노드는 프라이머리 노드로부터 변경 내용을 전달받아서 서로의 데이터를 동기화한다. 읽기 쿼리는 프라이머리 노드뿐만 아니라 필요하면 세컨드리 노드로 요청할 수 있다.

 

MongoDB 레플리카 셋은 항상 레플리카 셋에 포함된 노드 간 투표를 통해서 프라이머리 노드를 결정하므로 가능하면 홀수 개의 노드로 구성하는 것이 좋다. 짝수 개의 노드로도 구성할 수 있지만, 실제 홀수로 구성한 것과 가용성이 다르지 않으며 오히려 하나의 노드가 낭비된다. 또한 짝수로 구성하면 쿼럼 구성이 어려워질 수도 있다.

 

하지만 이런 생각을 할 수 있다. "레플리카 셋을 굳이 3대의 서버로 구축하는 것은 때로는 서버의 낭비일 수 있지 않나?" 이런 경우를 위해서 MongoDB서버를 아비터 모드로 실행할 수 있다. 아비터 모드로 시작되면 레플리카 셋의 노드들과 하트비트만 주고 받으며, 프라이머리 노드가 불능일 때, 아비터 모드가 아닌 세컨드리 노드중에 프라이머리 노드의 선출을 위한 투표에만 참여한다. 아비터는 로컬 디스크에 데이터를 저장하지 않고 프라이머리부터 데이터를 주고 받지 않기 때문에 고 사양의 장비가 필요하지 않다. 또한 아비터는 데이터를 가지고 있지 않으므로 프라이머리 노드로 선출될 수도 없다. 하나의 레플리카 셋에 여러개의 아비터 노드가 존재할 수는 있지만, 실제 정상적인 상태에서 하나 이상의 아비터는 필요하지 않다.

 

3)샤딩된 클러스터

샤딩된 클러스터 구조에서는 하나 이상의 레플리카 셋이 필요하며, 각 레플리카 셋은 자신만의 파티션된 데이터를 가지게 된다.(예를 들면 전체 데이터가 6이고, 레플리카 셋이 3개라면 각각 2의 데이터 파티션을 나누어 갖는다.) 샤딩된 클러스터에 참여하고 있는 각각의 레플리카 셋을 샤드라고 하는데, 이 샤드들이 어떤 데이터를 가지는지에 대한 정보는 MongoDB 컨피그(Config) 서버가 관리한다.

 

샤딩된 클러스터 구조에서는 위의 2가지 상황과 달리 MongoDB 클라이언트 드라이버가 직접 서버에 붙지 않고 MongoDB 라우터(mongos)에 연결된다. 그리고 라우터는 자동으로 MongoDB 컨피그 서버로부터 각 샤드가 가지고 있는 데이터에 대한 메타 정보들을 참조하여 쿼리를 실행한다. 말 그대로 라우터(mongos)는 사용자로부터 요청된 쿼리를 실제 데이터를 가지고 있는 샤드로 전달하는 역할을 수행한다. 하지만 이뿐만 아니라 라우터는 사용자를 대신하여 모든 샤드로부터 쿼리를 요청하고 결과를 정렬 및 병합해서 반환하는 처리도 수행한다. 라우터는 각 샤드간의 데이터가 재분배되는 시점에도 동일한 역할을 수행하여 사용자나 응용 프로그램이 알아채지 못하게 투명하게 데이터 리밸런싱 작업을 처리한다.

 

여기까지 간단하게 MongoDB에 대해 이론적인 내용으로 살펴보았다. 나만 느끼는 건지 모르겠는데, 분산처리,샤딩,복제 등을 지원하는 모든 솔루션들은 대부분 아키텍쳐가 비슷한 느낌이다.

 

 

 

MongoDB (RDB와 비교, 특징과 장단점, 메모리성능 이슈, 주요용어)

참조문서 : https://docs.mongodb.com/ mongoDB는 C++로 짜여진 오픈소스 데이터베이스이다. 문서지향(Document-Oriented)적이며 뛰어난 확장성과 성능을 자랑한다. NoSQL이다. 1. RDB와 비교 RDBMS MongoDB Datab..

sjh836.tistory.com

 

MongoDB의 Physical 데이타 저장 구조

MongoDB를 구성할때 보면, 가장 많이 권장 받는 부분 중의 하나가, 메모리량과 디스크 성능이다. 메모리 크기가 아주 sensitive한 요인이 되는데, 어떤 부분이 문제가 되는지 내부 저장 구조를 살펴 봄으로써 이해..

bcho.tistory.com

 

posted by 여성게
:
일상&기타/일상 2019. 9. 12. 16:46

 

새로운 회사 입사를 앞두고 공부해야 할 것, 공부하고 싶은 것들 정리.

 

언어

  1. Scala
  2. Kotlin
  3. Python

 

데이터

  1. Kafka(Stream)
  2. Elasticsearch
  3. Hadoop
  4. Hive
  5. Spark

 

그 외

  1. NoSQL(MongoDB..)
  2. Docker, Kubernetes
  3. MSA

 

열심히 해도 아직까지도 부족한 것들이 많다. IT에는 왜 이렇게 많은 기술들이 많을까? 아예 이런 것들을 하나로 패킹한 대단한 솔루션이 어디 없을까..박학다식이란 힘든 일이다.

 

posted by 여성게
: