-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 가 없는 경우 전체 샤드로 쿼리 전송하는 것 확인