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 모드가 있으니, 상황에 맞게 활용해 보시기 바랍니다.