NoSQL

MongoDB index Multi-Key Index

kkimdubi·2019년 10월 19일·조회 20,827
이번 글 에서는 multikey index에 대해 알아보겠습니다.
MongoDB는 Document 기반의 비정규화된 데이터를 저장하는 데이터베이스이기 때문에
하나의 document가 array형태의 데이터를 가지는 경우나, document안에 또 document가 있는 embeded document형태의 데이터를 갖는 경우가 많은데
이런 형태의 document를 위한 인덱스가 Multi-Key Index입니다. ### multikey index ~~~ > db.user.insert({ ... name:"kimdubi", ... addr:{ city : "seongnam", dong: "sampyeong" }, ... db:["MongoDB","Mysql","Oracle"]}) db.user.createIndex({addr:1}) { "createdCollectionAutomatically" : false, "numIndexesBefore" : 1, "numIndexesAfter" : 2, "ok" : 1 } db.user.createIndex({db:1}) { "createdCollectionAutomatically" : false, "numIndexesBefore" : 2, "numIndexesAfter" : 3, "ok" : 1 } > db.user.createIndex({"addr.city":1}) { "createdCollectionAutomatically" : false, "numIndexesBefore" : 3, "numIndexesAfter" : 4, "ok" : 1 } > db.user.createIndex({"addr.city":1,"addr.dong":1}) { "createdCollectionAutomatically" : false, "numIndexesBefore" : 4, "numIndexesAfter" : 5, "ok" : 1 ~~~ => array, embeded document 를 field 로 갖는 인덱스, embeded document의 특정 field만으로도 인덱싱 가능 ### embeded document / array field 조회하는 방법 * embeded document 정의문 그대로 넣기, 하나 빼거나 순서 다르면 실패 ~~~ db.user.find({addr:{city:"seongnam",dong:"sampyeong"}}).pretty() { "_id" : ObjectId("5daaccd874411c322449442e"), "name" : "kimdubi", "addr" : { "city" : "seongnam", "dong" : "sampyeong" }, "db" : [ "MongoDB", "Mysql", "Oracle" ] } > db.user.find({addr:{dong:"sampyeong"}}).pretty() > > db.user.find({addr:{city:"seongnam"}}).pretty() > ~~~ * embeded document의 field 로 호출 ~~~ > db.user.find({"addr.city":"seongnam"}) { "_id" : ObjectId("5daaccd874411c322449442e"), "name" : "kimdubi", "addr" : { "city" : "seongnam", "dong" : "sampyeong" }, "db" : [ "MongoDB", "Mysql", "Oracle" ] } > db.user.find({"addr.dong":"sampyeong"}) { "_id" : ObjectId("5daaccd874411c322449442e"), "name" : "kimdubi", "addr" : { "city" : "seongnam", "dong" : "sampyeong" }, "db" : [ "MongoDB", "Mysql", "Oracle" ] } ~~~ * array ~~~ > db.user.find({db:"MongoDB"}) { "_id" : ObjectId("5daaccd874411c322449442e"), "name" : "kimdubi", "addr" : { "city" : "seongnam", "dong" : "sampyeong" }, "db" : [ "MongoDB", "Mysql", "Oracle" ] } ~~~ ### Multi-Key index 제한 사항 * Multi-Key index는 shard key 로 사용될 수 없음 => 여러개의 인덱스 엔트리가 같은 document를 가리키기 때문에 shard key 로 사용할 수 없음
shard key 는 키 값의 범위에 따라 하나의 chunk로 매핑되어야 하기 때문 * Multi-Key index는 커버링 인덱스 처리가 불가능함 ~~~ db.installed_apps.getIndexes() [ { "v" : 2, "key" : { "_id" : 1 }, "name" : "_id_", "ns" : "test.installed_apps" }, { "v" : 2, "key" : { "apps" : 1 }, "name" : "apps_1", "ns" : "test.installed_apps" } ] db.installed_apps.explain().aggregate([ {'$unwind':'$apps'},{'$group':{_id:'$apps',sum:{'$sum':1}}},{'$match':{sum:{'$gte':2}}}]) { "stages" : [ { "$cursor" : { "query" : { }, "fields" : { "apps" : 1, "_id" : 0 }, "queryPlanner" : { "plannerVersion" : 1, "namespace" : "test.installed_apps", "indexFilterSet" : false, "parsedQuery" : { }, "queryHash" : "8B3D4AB8", "planCacheKey" : "8B3D4AB8", "winningPlan" : { "stage" : "COLLSCAN", "direction" : "forward" }, "rejectedPlans" : [ ] } } }, { "$unwind" : { "path" : "$apps" } }, { "$group" : { "_id" : "$apps", "sum" : { "$sum" : { "$const" : 1 } } } }, { "$match" : { "sum" : { "$gte" : 2 } } } ], "ok" : 1 } ~~~ => app field에 인덱스가 있지만 multi-key index는 커버링 인덱스 처리가 되지 않는다.
컬렉션을 모두 읽은 다음에 apps field 의 array 데이터를 그룹핑 해서 최종 결과를 반환하게됨 * Mulki-Key index 의 검색조건 bound ~~~ > db.test.insert({i:[2,9]}) WriteResult({ "nInserted" : 1 }) > db.test.insert({i:[4,3]}) WriteResult({ "nInserted" : 1 }) > > db.test.find({i:{$gte:3, $lte:6}}) { "_id" : ObjectId("5dab36f674411c3224494432"), "i" : [ 2, 9 ] } { "_id" : ObjectId("5dab36fb74411c3224494433"), "i" : [ 4, 3 ] } ~~~ => i:{$gte:3, $lte:6} 조건이니 [2,9] 는 조회되지 않을 것 같지만 포함되어있음 i : {$gte:3} i : {$lte:6} 의 합집합으로 조회되기 때문임 ~~~ > db.test.find({i:{$elemMatch:{$gte:3, $lte:6}}}) { "_id" : ObjectId("5dab36fb74411c3224494433"), "i" : [ 4, 3 ] } ~~~ => Mulki-key index 에 대해 between 검색을 하려면 array의 각 element들에 대해 $elemMatch 연산자를 사용해야함
i : {$gte:3} i : {$lte:6} 의 교집합으로 조회 ~~~ > db.test.insert({i:[2,4]}) WriteResult({ "nInserted" : 1 }) > db.test.find({i:{$elemMatch:{$gte:3,$lte:6}}}) { "_id" : ObjectId("5dab36fb74411c3224494433"), "i" : [ 4, 3 ] } { "_id" : ObjectId("5dab3a1874411c3224494434"), "i" : [ 2, 4 ] } ~~~ => $elemMatch연산자의 결과는 배열의 모든 요소가 조건에 만족해야 하는 게 아니라
$elemMatch의 조건을 모두 만족하는 엘리먼트 하나라도 가지고 있는 document를 반환함

댓글 0

로그인 후 댓글을 남길 수 있습니다.

아직 댓글이 없습니다.