본문 바로가기

MongoDB/R&D

MongoDB Plan Cache

1. MongoDB Plan Cache 사용 이유

MongoDB 에서 Execution Plan 은 101건의 document 를 반환하는 Plan 을 사용한다.

이 행위를 쿼리가 요청될 때마다, 매번 하는 것은 사실 비효율적인 행위이다.

 

그래서 MongoDB 에서는 Plan Cache 라는 개념을 사용한다. 이는 MySQL 의 Query Cache 랑은 다른 개념이다.


MongoDB 의 Plan Cache 는 Query Shape (쿼리 패턴이라고 이해해도 좋다.) 별 어떤 Execution Plan 을 사용할지 Caching 해두는 공간이다.

 

Plan Cache 가 처음부터 Plan 을 Caching 을 해두는 것은 아니고, 실제 쿼리가 실행 되었을 때 만들어진 Execution Plan 을 캐싱한다.

 

Caching 을 하며 동시에 내부적인 계산을 통해 해당 execution plan 의 스코어를 매겨 이 수치를 "feedback" 이란 필드에 값을 저장하게된다.

 

위와 같은 행위를 일정 횟수만큼 반복하면 샘플링은 종료되고, 특정 Query Shape 는 더 이상 Execution Plan 을 수집하지않고 Plan Cache 에 저장된 Plan 을 사용하는 형태이다.

 

따라서 동일한 Query Shape 여도 샘플링된 형태에 따라 다른 Execution Plan 을 가질 수 있다는 것이다.

Plan Cache 에 저장된 Plan 이 대부분 효율적이겠지만, 분명 비효율적인 Plan 으로 캐싱되었을 가능성도 여전히 존재한다.

 

그래서 MongoDB 에서는 Plan Cache 를 조회하고 Clearing 하는 Method 도 함께 제공한다.

 

> db.chats.getPlanCache().help() 

PlanCache help

db.chats.getPlanCache().help() - show PlanCache help 
db.chats.getPlanCache().listQueryShapes() - displays all query shapes in a collection 
db.chats.getPlanCache().clear() - drops all cached queries in a collection 
db.chats.getPlanCache().clearPlansByQuery(query[, projection, sort, collation]) - drops queryshape from plan cache
db.chats.getPlanCache().getPlansByQuery(query[, projection, sort, collation]) - displays the cached plans for a query shape

 

 

2. Query Shape

Plan Cache 에 저장된 query shape 의 list 는 아래와 같이 확인이 가능하다

> db.chats.getPlanCache().listQueryShapes()
[ 

   { 

      "query":{ 

         "?":231079977,
         "?":{ 

            "$lt":2032031492209680400

         

},
         "?":false,
         "?":false,
         "?":{ 

            "$gt":0

         

},
         "?":false

      

},
      "sort":{ 

         "?":-1

      

},
      "projection":{ 

 

      

}

   

}

]

 

MongoDB 에서는 위와 같은 Query Shape 별 Execution Plan 을 Plan Cache 에 caching 한다.

 

 

3. Get Plans By Query

해당 method 를 이용하면, Query Shape 별로 Plan Cache 에 저장된 Plan 들을 볼 수 있다.

 

 

더보기

> db.chats.getPlanCache().getPlansByQuery({ ?: 245524874, ?: { "$lt" :
    2029212740581423104 }, ?: false, ?: false, ?: false, ?: {"$gt": 0} }, {}, {"?":-1})
{
"plans" : [ {
"details" : {
"solution" : "(index-tagged expression tree: tree=Node\n---Leaf \n---Leaf \n---
Leaf \n---Leaf ix_1, pos: 0, can combine? 1\n---Leaf ix_1, pos: 1, can combine? 1\n---Leaf ix_1, pos: 2, can combine? 1\n)"
},
"reason" : {
"score" : 1.0000156641604008, "stats" : {
"stage" : "LIMIT",
"nReturned" : 0, "executionTimeMillisEstimate" : 80, "works" : 19152,
"advanced" : 0,
"needTime" : 19151,
"needYield" : 0,
"saveState" : 897,
"restoreState" : 897,
"isEOF" : 1,
"invalidates" : 0,
"limitAmount" : 101,
"inputStage" : {
"stage" : "FETCH", "filter" : {
"$and" : [ {
"?" : { "$eq" : false
} },
{
"?" : {
"$eq" : false }
}, {
} }
] },
"nReturned" : 0, "executionTimeMillisEstimate" : 70, "works" : 19152,
"advanced" : 0,
"needTime" : 19151,
"needYield" : 0,
"saveState" : 897,
"restoreState" : 897,
"isEOF" : 1,
"invalidates" : 0,
"?" : { "$eq" : false

   "docsExamined" : 0, "alreadyHasObj" : 0, "inputStage" : {
"stage" : "IXSCAN",
"nReturned" : 0, "executionTimeMillisEstimate" : 70, "works" : 19152,
"advanced" : 0,
"needTime" : 19151,
"needYield" : 0,
"saveState" : 897,
"restoreState" : 897,
"isEOF" : 1,
"invalidates" : 0,
"keyPattern" : {
"?" : 1, "?" : 1, "?" : 1
},
"indexName" : "ix_1", "isMultiKey" : false,
"multiKeyPaths" : {
"?" : [ ], "?" : [ ], "?" : [ ]
},
"isUnique" : false, "isSparse" : false, "isPartial" : false, "indexVersion" : 2, "direction" : "backward", "indexBounds" : {
"?" : [
"[231079977.0, 231079977.0]"
],
"?" : [
"(2.03203149220968e+18, -inf.0]" ],
"?" : [ "[inf.0, 0.0)"
] },
"keysExamined" : 19152, "seeks" : 19152, "dupsTested" : 0, "dupsDropped" : 0, "seenInvalidated" : 0
} }
} },
"feedback" : { "nfeedback" : 2, "scores" : [
{
"score" : 1.0003
},

   {
"score" : 1.0003
} ]
},
"filterSet" : false },
{
"details" : {
"solution" : "(index-tagged expression tree: tree=Node\n---Leaf \n---Leaf \n--- Leaf ix_2, pos: 1, can combine? 1\n---Leaf ix_2, pos: 0, can combine? 1\n---Leaf ix_2, pos: 2, can combine? 1\n---Leaf \n)"
},
"reason" : {
"score" : 1.0000156641604008, "stats" : {
"stage" : "LIMIT",
"nReturned" : 0, "executionTimeMillisEstimate" : 280, "works" : 19152,
"advanced" : 0,
"needTime" : 19151,
"needYield" : 0,
"saveState" : 897,
"restoreState" : 897,
"isEOF" : 1,
"invalidates" : 0,
"limitAmount" : 101,
"inputStage" : {
"stage" : "FETCH", "filter" : {
"$and" : [ {
"?" : { "$eq" : false
} },
{
"?" : {
"$eq" : false }
}, {
} }
] },
"nReturned" : 0, "executionTimeMillisEstimate" : 280, "works" : 19152,
"advanced" : 0,
"needTime" : 19151,
"needYield" : 0,
"saveState" : 897,
"restoreState" : 897,
"?" : { "$gt" : 0

   "isEOF" : 1,
"invalidates" : 0, "docsExamined" : 19151, "alreadyHasObj" : 0, "inputStage" : {
"stage" : "IXSCAN",
"nReturned" : 19151, "executionTimeMillisEstimate" : 20, "works" : 19152,
"advanced" : 19151,
"needTime" : 0,
"needYield" : 0,
"saveState" : 897,
"restoreState" : 897,
"isEOF" : 1,
"invalidates" : 0,
"keyPattern" : {
"?" : 1, "?" : 1, "?" : 1
},
"indexName" : "ix_2", "isMultiKey" : false, "multiKeyPaths" : {
"?" : [ ], "?" : [ ], "?" : [ ]
},
"isUnique" : false, "isSparse" : false, "isPartial" : false, "indexVersion" : 2, "direction" : "backward", "indexBounds" : {
"?" : [
"[231079977.0, 231079977.0]"
],
"?" : [
"[false, false]" ],
"?" : [ "(2.03203149220968e+18, -inf.0]"
] },
"keysExamined" : 19151, "seeks" : 1, "dupsTested" : 0, "dupsDropped" : 0, "seenInvalidated" : 0
} }
} },
"feedback" : {
},
"filterSet" : false

   }, {
ix_3, pos: 2, can combine? 1\n---Leaf \n--- Leaf ix_3, pos: 3, can combine? 1\n---Leaf ix_3, pos: 0, can combine? 1\n---Leaf ix_3, pos: 4, can combine? 1\n---Leaf \n)"
},
"reason" : {
"score" : 1.0000104427736007, "stats" : {
"stage" : "SORT",
"nReturned" : 0, "executionTimeMillisEstimate" : 70, "works" : 19152,
"advanced" : 0,
"needTime" : 19152,
"needYield" : 0,
"saveState" : 897,
"restoreState" : 897,
"isEOF" : 0,
"invalidates" : 0,
"sortPattern" : {
"?" : -1 },
"memUsage" : 0, "memLimit" : 33554432, "limitAmount" : 101, "inputStage" : {
"stage" : "SORT_KEY_GENERATOR", "nReturned" : 0, "executionTimeMillisEstimate" : 60, "works" : 19152,
"advanced" : 0, "needTime" : 19152, "needYield" : 0, "saveState" : 897, "restoreState" : 897, "isEOF" : 0, "invalidates" : 0, "inputStage" : {
"stage" : "FETCH", "filter" : {
"$and" : [ {
"?" : { "$eq" : false
} },
{
"?" : {
"$gt" : 0 }
} ]
"details" : {
"solution" : "(index-tagged expression tree: tree=Node\n---Leaf
},

   "nReturned" : 0, "executionTimeMillisEstimate" : 60, "works" : 19151,
"advanced" : 0,
"needTime" : 19151,
"needYield" : 0,
"saveState" : 897,
"restoreState" : 897,
"isEOF" : 0,
"invalidates" : 0,
"docsExamined" : 19150, "alreadyHasObj" : 0,
"inputStage" : {
"stage" : "IXSCAN",
"nReturned" : 19150, "executionTimeMillisEstimate" : 10, "works" : 19151,
"advanced" : 19150,
"needTime" : 1,
"needYield" : 0,
"saveState" : 897,
"restoreState" : 897,
"isEOF" : 0,
"invalidates" : 0,
"keyPattern" : {
"?" : 1, "assignee_id" : 1, "?" : 1, "?" : 1, "?" : 1
},
"indexName" : "ix_3",
"isMultiKey" : false, "multiKeyPaths" : { "?" : [ ],
"assignee_id" : [ ], "?" : [ ], "?" : [ ], "?" : [ ]
},
"isUnique" : false, "isSparse" : false, "isPartial" : false, "indexVersion" : 2, "direction" : "forward", "indexBounds" : {
"?" : [
"[231079977.0, 231079977.0]"
],
"assignee_id" : [
"[MinKey, MaxKey]" ],
"?" : [ "[false, false]"
],
"?" : [

   "[false, false]" ],
"?" : [
"[-inf.0, 2.03203149220968e+18)"
] },
"keysExamined" : 19151, "seeks" : 2, "dupsTested" : 0, "dupsDropped" : 0, "seenInvalidated" : 0
} }
} }
},
"feedback" : {
},
"filterSet" : false },
{
"details" : {
"solution" : "(index-tagged expression tree: tree=Node\n---Leaf \n---Leaf \n--- Leaf \n---Leaf ix_shardkey, pos: 0, can combine? 1\n---Leaf \n---Leaf \n)"
},
"reason" : {
"score" : 1.0000104427736007, "stats" : {
"stage" : "SORT",
"nReturned" : 0, "executionTimeMillisEstimate" : 160, "works" : 19152,
"advanced" : 0,
"needTime" : 19152,
"needYield" : 0,
"saveState" : 897,
"restoreState" : 897,
"isEOF" : 0,
"invalidates" : 0,
"sortPattern" : {
"?" : -1 },
"memUsage" : 0, "memLimit" : 33554432, "limitAmount" : 101, "inputStage" : {
"stage" : "SORT_KEY_GENERATOR", "nReturned" : 0, "executionTimeMillisEstimate" : 160, "works" : 19152,
"advanced" : 0, "needTime" : 19152, "needYield" : 0, "saveState" : 897, "restoreState" : 897, "isEOF" : 0,

   "invalidates" : 0, "inputStage" : {
"stage" : "FETCH", "filter" : {
"$and" : [ {
"?" : {
"$eq" : 231079977
} },
{
"?" : {
"$eq" : false }
}, {
} },
{
"?" : {
"$eq" : false }
}, {
} },
{
"?" : {
"$gt" : 0 }
} ]
},
"nReturned" : 0, "executionTimeMillisEstimate" : 160, "works" : 19151,
"advanced" : 0,
"needTime" : 19151,
"needYield" : 0,
"saveState" : 897,
"restoreState" : 897,
"isEOF" : 0,
"invalidates" : 0,
"docsExamined" : 19151, "alreadyHasObj" : 0,
"inputStage" : {
"stage" : "IXSCAN",
"nReturned" : 19151, "executionTimeMillisEstimate" : 30, "works" : 19151,
"advanced" : 19151,
"needTime" : 0,
"needYield" : 0,
"?" : { "$eq" : false
"?" : {
"$lt" : 2032031492209680400

   "saveState" : 897, "restoreState" : 897, "isEOF" : 0, "invalidates" : 0, "keyPattern" : {
"?" : "hashed" },
"indexName" : "ix_shardkey", "isMultiKey" : false, "isUnique" : false,
"isSparse" : false,
"isPartial" : false, "indexVersion" : 2, "direction" : "forward", "indexBounds" : {
"?" : [
"[-7197939591252853898, -7197939591252853898]"
] },
"keysExamined" : 19151, "seeks" : 1, "dupsTested" : 0, "dupsDropped" : 0, "seenInvalidated" : 0
} }
} }
},
"feedback" : {
},
"filterSet" : false },
{
"details" : {
"solution" : "(index-tagged expression tree: tree=Node\n---Leaf \n---Leaf \n--- Leaf \n---Leaf ix_4, pos: 0, can combine? 1\n---Leaf ix_4, pos: 2, can combine? 1\n---Leaf \n)"
},
"reason" : {
"score" : 1.0000104427736007, "stats" : {
"stage" : "SORT",
"nReturned" : 0, "executionTimeMillisEstimate" : 240, "works" : 19152,
"advanced" : 0,
"needTime" : 19152,
"needYield" : 0,
"saveState" : 897,
"restoreState" : 897,
"isEOF" : 0,
"invalidates" : 0,
"sortPattern" : {
"?" : -1 },

   "memUsage" : 0, "memLimit" : 33554432, "limitAmount" : 101, "inputStage" : {
"stage" : "SORT_KEY_GENERATOR", "nReturned" : 0, "executionTimeMillisEstimate" : 240, "works" : 19152,
"advanced" : 0, "needTime" : 19152, "needYield" : 0, "saveState" : 897, "restoreState" : 897, "isEOF" : 0, "invalidates" : 0, "inputStage" : {
"stage" : "FETCH", "filter" : {
"$and" : [ {
"?" : { "$eq" : false
} },
{
"?" : {
"$eq" : false }
}, {
} },
{
"?" : {
"$gt" : 0 }
} ]
},
"nReturned" : 0, "executionTimeMillisEstimate" : 240, "works" : 19151,
"advanced" : 0,
"needTime" : 19151,
"needYield" : 0,
"saveState" : 897,
"restoreState" : 897,
"isEOF" : 0,
"invalidates" : 0,
"docsExamined" : 19149, "alreadyHasObj" : 0,
"inputStage" : {
"stage" : "IXSCAN",
"nReturned" : 19149, "executionTimeMillisEstimate" : 40,
"?" : { "$eq" : false

   "works" : 19151, "advanced" : 19149, "needTime" : 2, "needYield" : 0, "saveState" : 897, "restoreState" : 897, "isEOF" : 0, "invalidates" : 0, "keyPattern" : {
"?" : 1, "is_starred" : 1, "?" : 1
},
"indexName" : "ix_4", "isMultiKey" : false,
"multiKeyPaths" : {
"?" : [ ], "is_starred" : [ ], "?" : [ ]
},
"isUnique" : false, "isSparse" : false, "isPartial" : false, "indexVersion" : 2, "direction" : "forward", "indexBounds" : {
"?" : [
"[231079977.0, 231079977.0]"
],
"is_starred" : [
"[MinKey, MaxKey]" ],
"?" : [
"[-inf.0, 2.03203149220968e+18)"
] },
"keysExamined" : 19151, "seeks" : 3, "dupsTested" : 0, "dupsDropped" : 0, "seenInvalidated" : 0
} }
} }
},
"feedback" : {
},
"filterSet" : false },
{
"details" : {
"solution" : "(index-tagged expression tree: tree=Node\n---Leaf \n---Leaf \n--- Leaf \n---Leaf ix_5, pos: 0, can combine? 1\n---Leaf ix_5, pos: 2, can combine? 1\n---Leaf \n)"
},

   "reason" : {
"score" : 1.0000104427736007, "stats" : {
"stage" : "SORT",
"nReturned" : 0, "executionTimeMillisEstimate" : 420, "works" : 19152,
"advanced" : 0,
"needTime" : 19152,
"needYield" : 0,
"saveState" : 897,
"restoreState" : 897,
"isEOF" : 0,
"invalidates" : 0,
"sortPattern" : {
"?" : -1 },
"memUsage" : 0, "memLimit" : 33554432, "limitAmount" : 101, "inputStage" : {
"stage" : "SORT_KEY_GENERATOR", "nReturned" : 0, "executionTimeMillisEstimate" : 420, "works" : 19152,
"advanced" : 0, "needTime" : 19152, "needYield" : 0, "saveState" : 897, "restoreState" : 897, "isEOF" : 0, "invalidates" : 0, "inputStage" : {
"stage" : "FETCH", "filter" : {
"$and" : [ {
"?" : { "$eq" : false
} },
{
"?" : {
"$eq" : false }
}, {
} },
{
"?" : {
"$gt" : 0 }
} ]
"?" : { "$eq" : false

   },
"nReturned" : 0, "executionTimeMillisEstimate" : 420, "works" : 19151,
"advanced" : 0,
"needTime" : 19151,
"needYield" : 0,
"saveState" : 897,
"restoreState" : 897,
"isEOF" : 0,
"invalidates" : 0,
"docsExamined" : 19150, "alreadyHasObj" : 0,
"inputStage" : {
"stage" : "IXSCAN",
"nReturned" : 19150, "executionTimeMillisEstimate" : 20, "works" : 19151,
"advanced" : 19150,
"needTime" : 1,
"needYield" : 0,
"saveState" : 897,
"restoreState" : 897,
"isEOF" : 0,
"invalidates" : 0,
"keyPattern" : {
"?" : 1, "chat_label_ids" : 1, "?" : 1
},
"indexName" : "ix_5", "isMultiKey" : true,
"multiKeyPaths" : {
"?" : [ ], "chat_label_ids" : [
"chat_label_ids" ],
"?" : [ ] },
"isUnique" : false, "isSparse" : false, "isPartial" : false, "indexVersion" : 2, "direction" : "forward", "indexBounds" : {
"?" : [
"[231079977.0, 231079977.0]"
],
"chat_label_ids" : [
"[MinKey, MaxKey]" ],
"?" : [
"[-inf.0, 2.03203149220968e+18)"
] },
"keysExamined" : 19151, "seeks" : 2,

"dupsTested" : 19150, "dupsDropped" : 0, "seenInvalidated" : 0
} }
} }
},
"feedback" : {
},
"filterSet" : false }
],
"timeOfCreation" : ISODate("2019-07-18T09:10:53.638Z"), "ok" : 1,
"$gleStats" : {
"lastOpTime" : Timestamp(0, 0),
"electionId" : ObjectId("000000000000000000000000") },
"$configServerState" : { "opTime" : {
"ts" : Timestamp(1563441130, 1),
"t" : NumberLong(6) }
} }

Plan 뿐만 아닌 Score 라는 수치도 곳곳에 나와있는 것을 볼 수 있다.
특히 가장 처음에 있는 계획의 필드 중에서 "feedback" 이라는 필드에는 스코어가 집계 되고 있는 것을 볼 수 있다

 

 

> db.chats.getPlanCache().getPlansByQuery({ ?: 245524874, ?: { "$lt" : 2029212740581423104 }, ?: false, ?: false, ?: false, ?: {"$gt": 0} }, {}, {"?":-1})
...
...
"feedback" : { "nfeedback" : 2, "scores" : [
{
"score" : 1.0003
}, 
{
"score" : 1.0003 }
] },
...
...

 

현재는 "nfeedback" 필드 value 값으로 2 로 2개만 나타나지만, 해당 필드가 20 이 될 때까지 스코어링을 지속한다. 총 20개의 샘플링이 모두 완료되면, 더 이상 수집을 하지않고 샘플링을 종료한다.

 

 

4. 샘플링이 종료되어 Plan Caching 이 완료된 케이스

> db.chats.getPlanCache().getPlansByQuery({ ?: 245524874, ?: { "$lt" : 2029212740581423104 }, ?: false, ?: false, ?: false, ?: {"$gt": 0} }, {}, {"?":-1})

{
"plans" : [ {
"details" : {
"solution" : "(index-tagged expression tree: tree=Node\n---Leaf \n---Leaf \n---
Leaf ix_1, pos: 1, can combine? 1\n---Leaf ix_1, pos: 0, can combine? 1\n---Leaf ix_1, pos: 2, can combine? 1\n---Leaf \n)"
},
"reason" : {
"score" : 1.0000021372544827, "stats" : {
... 
...

Plan Cache 에 저장된 첫번째 Exeuction Plan 이면서 동시에 해당 Query Shape 의 return value 이다. score 는 "1.0000021372544827" 이라는 값을 가진다.

그 밑에 feedback 수치를 살펴본다.

 

"feedback" : { "nfeedback" : 20, "scores" : [
{
"score" : 1.0002
}, {
"score" : 1.0000143235694334 },
{
"score" : 1.0000086685159502
}, {
"score" : 1.000011201971547 },
{
"score" : 1.000002137254483
}, {
"score" : 1.000002137254483 },
{
"score" : 1.000002137254483
}, {
"score" : 1.000002137254483 },
{
"score" : 1.0000100381449508
}, {
"score" : 1.0000100583383624 },
{
"score" : 1.0002
}, {
"score" : 1.0000009982131983 },
{
"score" : 1.000000993176875
}, {
"score" : 1.0000021372773225 },
{
"score" : 1.0000021372773225
}, {
"score" : 1.0000021372773225 },
{
"score" : 1.0000021372773225
}, {
"score" : 1.0000097967180994 },
{
"score" : 1.0000095438060699
}, {
"score" : 1.0000112007168458 }
] },

위 값들 중에서 가장 낮은 score 값을 가진 수치는 "1.000002137254483" 이다.

그리고 이 값은 첫번째 Execution Plan 에서 나타난 score 값이다.


※ 첫번째 Execution Plan 의 Score : "1.0000021372544827" feedback 리스트 중 가장 낮은 Score : "1.000002137254483"(반올림)

 

 

5. Clear / Clear Plans By Query

Execution Plan 을 세울 때, 데이터 분포도에 따라 101건을 반환하는 계획은 매번 바뀔 수 있다.

이는 즉 동일한 Query Shape 이라도 스코어 값이 매번 바뀔 수 있다는 점이다.

 

그래서 Plan Cache 가 항상 효율적인 Plan 을 return 한다고 보장할 수 없다.
이 경우 DBA 의 판단에 따라 Plan Cache 를 Clearing 하고 새로운 Execution Plan 을 Caching 하도록 유도할 수 있다.

 

MongoDB 에서는 Plan Cache 를 Clearing 을 하기 위해 2가지 method 를 제공한다.

 

"clear()" 를 하게 되면 Plan Cache 에 저장된 모든 Query Shape 의 Plan 을 삭제한다.

하지만 일부의 쿼리만 비효율적으로 caching 되어있을 수도 있기때문에, 특정 Query Shape 만 Clearing 할 수도 있다.

 

이를 위한 method 가 "clearPlansByQuery()" 이다.

 

** Plan Cache 조회
> db.chats.getPlanCache().getPlansByQuery({ ?: 245524874, ?: { "$lt" : 2029212740581423104 }, ?: false, ?: false, ?: false, ?: {"$gt": 0} }, {}, {"?":-1})
{
"plans" : [ {
"details" : {
"solution" : "(index-tagged expression tree: tree=Node\n---Leaf \n---Leaf \n---
Leaf \n---Leaf ix_1, pos: 0, can combine? 1\n---Leaf ix_1, pos: 1, can combine? 1\n---Leaf ix_1, pos: 2, can combine? 1\n)"
},
"reason" : {
"score" : 1.0000156641604008, "stats" : {
... ... ...


** Plan Cache Celaring
> db.chats.getPlanCache().clearPlansByQuery({ ?: 245524874, ?: { "$lt" : 2029212740581423104 }, ?: false, ?: false, ?: false, ?: {"$gt": 0} }, {}, {"?":-1})


** Plan Cache 조회
> db.chats.getPlanCache().getPlansByQuery({ ?: 245524874, ?: { "$lt" : 2029212740581423104 }, ?: false, ?: false, ?: false, ?: {"$gt": 0} }, {}, {"?":-1})
{
"plans" : [ ], "ok" : 1, "$gleStats" : {
"lastOpTime" : Timestamp(0, 0),
"electionId" : ObjectId("000000000000000000000000") },
"$configServerState" : { "opTime" : {
"ts" : Timestamp(1563442240, 2),
"t" : NumberLong(6) }
} }

 

 

6. 정리

MongoDB 의 Optimizer 는 Rule Based Optimizer 의 단점을 보완하기위해 나름의 방법을 사용했다.(실제 조금만 실행해보는)

 

Rule Based 방식에서는 Histogram 같은 개념들을 이용하여, 데이터 분포도에 대한 문제 사항들을 해결하는 등의 방법을 도입하지않는 이상 데이터 분포도에 대한 단점을 극복 하기가 쉽지않다.

그래서 현재 MongoDB 에서 사용하는 Optimizer 방식은 개인적인 생각으로 꽤나 합리적인 방법이라고 생각한다.

 

 

하지만 위에서 설명한 Plan 을 캐싱하는 방식에서는 항상 효율적인 Execution Plan 을 세울 수가 없다.


게다가 이 문제에 대한 개선도 구조 상 쉽지가 않아보인다. (더 높은 version 의 mongodb new feature 에서도 관련 내용이 보이지도 않고..)

따라서 문제가 되는 케이스에선 Clearing 하고 다시 reCaching 하는 DBA 의 수작업이 필요할 수 밖에 없는 듯 하다.

'MongoDB > R&D' 카테고리의 다른 글

MongoDB Replica Set Protocol Version (PV)  (0) 2020.02.12
MongoDB Authentication Mechanism  (0) 2020.02.03
MongoDB Query Execution Plan  (1) 2020.01.29