Computer >> 컴퓨터 >  >> 프로그래밍 >> MongoDB

MongoDB 쿼리 계획(Query Plan) 완벽 이해하기 – explain() 메서드 활용 가이드

MongoDB에서 쿼리가 실제로 어떻게 실행되는지 확인하고 싶다면 explain() 메서드를 사용하면 됩니다. explain()은 쿼리 플래너(query planner)가 선택한 실행 계획, 사용된 인덱스, 검사한 문서 수 등의 상세 정보를 제공하여 쿼리 성능을 분석하고 최적화하는 데 큰 도움이 됩니다.

1. 테스트 컬렉션 생성 및 인덱스 설정

먼저 예제로 사용할 컬렉션을 만들고 문서를 삽입한 뒤, 인덱스를 생성해 보겠습니다.

> db.demo408.insertOne({"Value":50});
{
    "acknowledged" : true,
    "insertedId" : ObjectId("5e70e3a115dc524f70227678")
}
> db.demo408.insertOne({"Value":20});
{
    "acknowledged" : true,
    "insertedId" : ObjectId("5e70e3a715dc524f70227679")
}
> db.demo408.insertOne({"Value":45});
{
    "acknowledged" : true,
    "insertedId" : ObjectId("5e70e3ac15dc524f7022767a")
}
> db.demo408.insertOne({"Value":35});
{
    "acknowledged" : true,
    "insertedId" : ObjectId("5e70e3af15dc524f7022767b")
}
> db.demo408.createIndex({Value:1});
{
    "createdCollectionAutomatically" : false,
    "numIndexesBefore" : 1,
    "numIndexesAfter" : 2,
    "ok" : 1
}

위 코드에서는 Value 필드에 오름차순 인덱스를 생성했습니다. 인덱스가 정상적으로 추가되어 인덱스 개수가 1개에서 2개로 늘어난 것을 확인할 수 있습니다.

2. 저장된 문서 조회하기

find() 메서드를 사용해 컬렉션의 모든 문서를 출력합니다.

> db.demo408.find();

실행 결과는 다음과 같습니다.

{ "_id" : ObjectId("5e70e3a115dc524f70227678"), "Value" : 50 }
{ "_id" : ObjectId("5e70e3a715dc524f70227679"), "Value" : 20 }
{ "_id" : ObjectId("5e70e3ac15dc524f7022767a"), "Value" : 45 }
{ "_id" : ObjectId("5e70e3af15dc524f7022767b"), "Value" : 35 }

3. explain()으로 쿼리 계획 확인하기

이제 explain() 메서드를 활용해 쿼리 실행 계획을 살펴보겠습니다. 아래 쿼리는 Value 값이 40보다 큰 문서를 찾는 조건입니다.

> db.demo408.explain().find( { Value: { $gt: 40 } } );

실행 결과는 다음과 같습니다.

{
    "queryPlanner" : {
        "plannerVersion" : 1,
        "namespace" : "test.demo408",
        "indexFilterSet" : false,
        "parsedQuery" : {
            "Value" : {
                "$gt" : 40
            }
        },
        "winningPlan" : {
            "stage" : "FETCH",
            "inputStage" : {
                "stage" : "IXSCAN",
                "keyPattern" : {
                    "Value" : 1
                },
                "indexName" : "Value_1",
                "isMultiKey" : false,
                "multiKeyPaths" : {
                    "Value" : [ ]
                },
                "isUnique" : false,
                "isSparse" : false,
                "isPartial" : false,
                "indexVersion" : 2,
                "direction" : "forward",
                "indexBounds" : {
                    "Value" : [
                        "(40.0, inf.0]"
                    ]
                }
            }
        },
        "rejectedPlans" : [ ]
    },
    "serverInfo" : {
        "host" : "DESKTOP-QN2RB3H",
        "port" : 27017,
        "version" : "4.0.5",
        "gitVersion" : "3739429dd92b92d1b0ab120911a23d50bf03c412"
    },
    "ok" : 1
}

4. 결과 해석하기

출력 결과의 핵심 부분은 winningPlan입니다. MongoDB 쿼리 플래너가 여러 후보 계획 중 최종적으로 선택한 실행 계획을 의미합니다.

  • stage: FETCH – 인덱스 스캔 결과를 바탕으로 실제 문서를 디스크에서 가져오는 단계입니다.
  • inputStage: IXSCAN – 인덱스 스캔(Index Scan)이 먼저 수행되었음을 보여줍니다. 즉, 컬렉션 전체를 훑는 COLLSCAN(컬렉션 스캔)이 아니라 생성된 인덱스를 활용했다는 뜻입니다.
  • indexName: Value_1 – 앞서 createIndex()로 만든 Value 오름차순 인덱스가 사용되었습니다.
  • indexBounds: (40.0, inf.0] – 인덱스 탐색 범위가 40 초과부터 무한대까지임을 나타냅니다. $gt 조건이 인덱스 범위로 정확히 반영된 것입니다.

또한 rejectedPlans가 비어 있는 것으로 보아, 다른 경쟁 실행 계획 없이 해당 인덱스 기반 계획이 곧바로 채택되었음을 알 수 있습니다.

마무리

explain()은 MongoDB 쿼리 최적화의 필수 도구입니다. 쿼리가 인덱스를 제대로 활용하는지, 불필요한 컬렉션 스캔이 발생하지 않는지 주기적으로 점검하면 대용량 데이터 환경에서도 안정적인 성능을 유지할 수 있습니다. 참고로 explain()에는 기본 모드 외에도 실제 실행까지 수행하는 executionStats 모드와 모든 계획을 비교하는 allPlansExecution 모드가 있으니, 상황에 맞게 활용해 보시기 바랍니다.