Tech/DB
MongoDB 쿼리 플랜 (간단하게!)
해리리_
2024. 3. 29. 17:57
Query Explain Plan
https://www.mongodb.com/ko-kr/docs/rapid/tutorial/analyze-query-plan/
explain result를 보고 query에 대한 아래 정보를 얻을 수 있다.
- 쿼리 수행에 걸리는 시간
- 쿼리가 인덱스를 사용하는지
- 쿼리 수행을 위해 스캔되는 문서나 인덱스 키의 개수
쿼리 성능에 대한 통계를 제공하는 메소드
- [cursor.explain("executionStats")] & [db.collection.explain("executionStats")]
- 쿼리가 인덱스를 사용하는지 여부와 방법을 측정할 때 유용
아래 document가 들어있다고 가정하자.
{ "_id" : 1, "item" : "f1", type: "food", quantity: 500 }
{ "_id" : 2, "item" : "f2", type: "food", quantity: 100 }
{ "_id" : 3, "item" : "p1", type: "paper", quantity: 200 }
{ "_id" : 4, "item" : "p2", type: "paper", quantity: 150 }
{ "_id" : 5, "item" : "f3", type: "food", quantity: 300 }
{ "_id" : 6, "item" : "t1", type: "toys", quantity: 500 }
{ "_id" : 7, "item" : "a1", type: "apparel", quantity: 250 }
{ "_id" : 8, "item" : "a2", type: "apparel", quantity: 400 }
{ "_id" : 9, "item" : "t2", type: "toys", quantity: 50 }
{ "_id" : 10, "item" : "f4", type: "food", quantity: 75 }
인덱스가 없는 쿼리
db.inventory.find( { quantity: { $gte: 100, $lte: 200 } } )
위 쿼리의 결과는 아래와 같다.
{ "_id" : 2, "item" : "f2", "type" : "food", "quantity" : 100 }
{ "_id" : 3, "item" : "p1", "type" : "paper", "quantity" : 200 }
{ "_id" : 4, "item" : "p2", "type" : "paper", "quantity" : 150 }
위 쿼리에 대한 explain을 보려면
db.inventory.find({ quantity: { $gte: 100, $lte: 200 } }).explain("executionStats")
과 같이 뒤에 explain을 붙여주면 된다.
그러면 아래 결과를 반환한다.
{
queryPlanner: {
...
winningPlan: {
queryPlan: {
stage: 'COLLSCAN',
...
}
}
},
executionStats: {
executionSuccess: true,
nReturned: 3,
executionTimeMillis: 0,
totalKeysExamined: 0,
totalDocsExamined: 10,
executionStages: {
stage: 'COLLSCAN',
...
},
...
},
...
}
- queryPlanner.winningPlan.queryPlan.stage : COLLSCAN
- 컬렉션을 스캔했다는 뜻. 즉 일반적으로 비용이 많이 드는 작업이므로 쿼리 속도가 느려질 수 있다는 말…
- executionStats.nReturned : 3
- 3개의 document를 반환했다는 뜻
- executionStats.totalKeysExamined : 0
- 쿼리가 인덱스를 사용하지 않고 있음을 나타낸다.
- executionStats.totalDocsExamined: 10
- 몽고디비가 3개의 document를 찾기 위해 10개의 document를 스캔해야 했다는 말임. 즉, 현재로써는 전체 document를 스캔했다는 말.
index를 적용해보자
현재까지 quantity를 기준으로 필터링해서 document를 조회했다. 이제 quantity 필드에 인덱스를 적용해보자.
db.inventory.createIndex( { quantity: 1 } )
여기서 다시 쿼리 플랜을 보자.
db.inventory.find({ quantity: { $gte: 100, $lte: 200 } }).explain("executionStats")
{
queryPlanner: {
...
winningPlan: {
queryPlan: {
stage: 'FETCH',
inputStage: {
stage: 'IXSCAN',
keyPattern: {
quantity: 1
},
...
}
}
},
rejectedPlans: [ ]
},
executionStats: {
executionSuccess: true,
nReturned: 3,
executionTimeMillis: 0,
totalKeysExamined: 3,
totalDocsExamined: 3,
executionStages: {
...
},
...
},
...
}
- queryPlanner.winningPlan.queryPlan.stage : IXSCAN
- 인덱스 사용했단 뜻
- executionStats.nReturned : 3
- 3개의 document를 반환했다는 뜻
- executionStats.totalKeysExamined : 3
- 세 개의 인덱스 항목을 스캔했다는 뜻. key 수는 반환된 document 수와 일치한다.
- 즉, 결과를 반환하기 위해 모든 문서를 스캔할 필요 없이 index key만 검사했다는 말이다.
- executionStats.totalDocsExamined: 3
- 3개의 문서를 스캔했단 뜻.
인덱스가 없으면 쿼리는 전체 10개 도큐먼트를 검색해서 일치하는 3개 도큐먼트를 반환한다. 또한 쿼리는 각 문서를 전체 스캔해서 잠재적으로 해당 문서를 메모리로 가져와야 했다. 이로 인해 비용이 많이 들고 쿼리 작업이 느려질 수 있다.
인덱스와 함께 실행하면 3개의 도큐먼트를 찾기 위해 쿼리가 3개의 인덱스 항목과 3개의 도큐먼트를 스캔했다. 매우 효율적인 쿼리인 것임.
다양한 stage 종류들은 아래에서 확인!
https://github.com/gharriso/MongoDBPerformanceTuningBook/blob/master/ExplainPlanSteps.md
728x90