지나공 : 지식을 나누는 공간

MongoDB 쿼리 플랜 (간단하게!) 본문

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개의 도큐먼트를 스캔했다. 매우 효율적인 쿼리인 것임.

728x90
Comments