Print
카테고리: [ NoSQL ]
조회수: 842
 

-1. mongus 란?

mongus 는 mongodb shard cluster 내에서 router 역할을 하는 컴포넌트로 아래 역할을 수행함
 
1) 쿼리 라우팅
2) 쿼리 결과 merge 후 client에게 return
* order by 같은 경우에는 샤드 서버 중 프라이머리 샤드를 정한 후, 그 프라이머리 샤드가 다른 샤드로부터 쿼리 결과를 전달받고 
먼저 order by 수행한 후에 최종 결과를 라우터로 반환함
 
2.x 버전에선 chunk 리밸런싱, 스플릿 등 chunk 관련 역할도 했으나 3.4 버전부터는 해당 역할이 모두
config 서버에서 담당하게 되어 mongus 는 말그대로 router 역할만 하게됨
 
* 줄어든 역할로 서버의 리소스를 매우 적게 사용하기 때문에 아래와 같이 mongus 를 다른 컴포넌트들과 같이 구성하기도 함
 
1) 어플리케이션 서버 n대 + mongus n대
=> 어플리케이션 서버와 mongus를 같은 서버에 둬서 어플리케이션 서버가 로컬호스트의 mongus 로 접속하게 되는 구조
2) config + mongus
3) mongus + shard 서버
4) mongus 만 따로 구성
 
* mongodb 에서는 mongus down 시 mongus 를 바라보는 어플리케이션도 어차피 역할을 못하기 때문에 한 세트의 의미로 1번의 구조를 추천하지만 
이슈가 발생했을 때 원인 분석, VM의 성능 향상과 관리상의 이유 등으로 mongus 를 따로 구성하기도 함
 

-2. 쿼리 라우팅

MongoDB shard cluster 에서 collection은 특정 필드 , 즉 shard key 를 기준으로 쪼개져서 여러 샤드에 분산되어 저장됨
이 단위를 chunk 라고 부르며 어느 shard에 어떤 chunk 가 있는지 , chunk 의 max key , min key 범위는 어떻게 되는지 같은 메타정보를 config 서버에서 읽어오고
사용자의 쿼리 내 샤드 키에 따라 mongus 는 특정 샤드에만 쿼리를 전달하는 타겟 쿼리 / 전체 샤드에 쿼리를 전달하는 브로드캐스트 쿼리로 요청을 처리하게 됨
 
1) 테스트 데이터 준비
 
mongos> use routing_test;
switched to db routing_test

mongos> sh.enableSharding('routing_test');
{
"ok" : 1,
"operationTime" : Timestamp(1567941332, 3),
"$clusterTime" : {
"clusterTime" : Timestamp(1567941332, 3),
"signature" : {
"hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
"keyId" : NumberLong(0)
}
}
}

mongos> sh.shardCollection('routing_test.employee',{id:1});
{
"collectionsharded" : "routing_test.employee",
"collectionUUID" : UUID("16ee8e2c-3b16-4ca2-879f-159cc45af712"),
"ok" : 1,
"operationTime" : Timestamp(1567941591, 3),
"$clusterTime" : {
"clusterTime" : Timestamp(1567941591, 3),
"signature" : {
"hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
"keyId" : NumberLong(0)
}
}
}
 
=> routing_test database & employee collection 생성 후 샤딩 설정
 
 
mongos> for (var i=0; i<200000; i++){
... db.employee.save( {id:i, name:"kim"+i, dept: "DB"+i })};
WriteResult({ "nInserted" : 1 })
 
=> 테스트 데이터 생성
 
mongos> sh.splitAt("routing_test.employee",{id:50000})
mongos> sh.splitAt("routing_test.employee",{id:100000})
mongos> sh.splitAt("routing_test.employee",{id:150000})
 
=> sh.shardCollection('routing_test.employee',{id:1});
 
range sharding 으로 collection 생성 했기 때문에 chunk 를 범위 지정하여 split 수행
sharding 방법에는 range , hash , tag 가 있으며 해당 내용은 추후 다룰 예정
range, hash sharding 은 테이블 파티셔닝과 동일한 개념임
 
mongos> use config;
switched to db config

mongos> db.chunks.find()

{ "_id" : "routing_test.employee-id_MinKey", "lastmod" : Timestamp(2, 0), "lastmodEpoch" : ObjectId("5d74e3d606d6020a9ea17680"), "ns" : "routing_test.employee", "min" : { "id" : { "$minKey" : 1 } }, "max" : { "id" : 50000 }, "shard" : "repl_shard1", "history" : [ { "validAfter" : Timestamp(1567943966, 26236), "shard" : "repl_shard1" } ] }

{ "_id" : "routing_test.employee-id_50000.0", "lastmod" : Timestamp(3, 0), "lastmodEpoch" : ObjectId("5d74e3d606d6020a9ea17680"), "ns" : "routing_test.employee", "min" : { "id" : 50000 }, "max" : { "id" : 100000 }, "shard" : "repl_shard1", "history" : [ { "validAfter" : Timestamp(1567943973, 6739), "shard" : "repl_shard1" } ] }

{ "_id" : "routing_test.employee-id_100000.0", "lastmod" : Timestamp(3, 1), "lastmodEpoch" : ObjectId("5d74e3d606d6020a9ea17680"), "ns" : "routing_test.employee", "min" : { "id" : 100000 }, "max" : { "id" : 150000 }, "shard" : "repl_shard2", "history" : [ { "validAfter" : Timestamp(1567941590, 6), "shard" : "repl_shard2" } ] }

{ "_id" : "routing_test.employee-id_150000.0", "lastmod" : Timestamp(2, 3), "lastmodEpoch" : ObjectId("5d74e3d606d6020a9ea17680"), "ns" : "routing_test.employee", "min" : { "id" : 150000 }, "max" : { "id" : { "$maxKey" : 1 } }, "shard" : "repl_shard2", "history" : [ { "validAfter" : Timestamp(1567941590, 6), "shard" : "repl_shard2" } ] }

 
=> routing_test.employee-id_MinKey , routing_test.employee-id_50000.0 , routing_test.employee-id_100000.0 , routing_test.employee-id_150000.0
총 4개의 chunk 가 생성되었으며 각각 min / max 값에 포함되는 데이터들이 해당 chunk 에 들어가게됨
 
 
2 ) 타겟 쿼리 
 
mongos> db.employee.find({id:1}).explain()
{
"queryPlanner" : {
"mongosPlannerVersion" : 1,
"winningPlan" : {
"stage" : "SINGLE_SHARD",
"shards" : [
{
"shardName" : "repl_shard1",
"connectionString" : "repl_shard1/mongo_shard1:27018,mongo_shard2:27018,mongo_shard3:27018",
"serverInfo" : {
"host" : "7cd58cda5dcd",
"port" : 27018,
"version" : "4.1.13",
"gitVersion" : "441714bc4c70699950f3ac51a5cac41dcd413eaa"
},
.
.
.
 
 
=> query 에 shard key 인 id 값이 포함되어 있으며 해당 chunk 를 갖고 있는 shard 에만 쿼리를 전송한 것 확인
 
 
3) 브로드캐스트 쿼리
 
mongos> db.employee.find({name:"kim1"}).explain()

{
"queryPlanner" : {
"mongosPlannerVersion" : 1,
"winningPlan" : {
"stage" : "SHARD_MERGE",
"shards" : [
{
"shardName" : "repl_shard1",
"connectionString" : "repl_shard1/mongo_shard1:27018,mongo_shard2:27018,mongo_shard3:27018",
"serverInfo" : {
"host" : "7cd58cda5dcd",
"port" : 27018,
"version" : "4.1.13",
"gitVersion" : "441714bc4c70699950f3ac51a5cac41dcd413eaa"
},
.
.
{
"shardName" : "repl_shard2",
"connectionString" : "repl_shard2/mongo_shard4:27018,mongo_shard5:27018,mongo_shard6:27018",
"serverInfo" : {
"host" : "d5904a7bf11d",
"port" : 27018,
"version" : "4.1.13",
"gitVersion" : "441714bc4c70699950f3ac51a5cac41dcd413eaa"
},
 
 
=> query 에 shard key 가 없는 경우 전체 샤드로 쿼리 전송하는 것 확인