본문으로 바로가기

인덱스를 만들었다면, 해당 인덱스를 mongoDB가 활용하고 있는 지를 체크해야 한다.

 

explain

 

find와 aggregate에서 explain하는 방식이 조금 다릅니다.

db.by_type.find().explain()
db.by_type..explain().aggregate([])

 

 

다음과 같이 쿼리 조건문을 이용하여 특정 정보를 find한 후에 explain()을 하였다.

db.by_type.find(
  {type: "차대차", accident_count: {$gte: 100}, death_toll: 0},
  {city_or_province: 1, county: 1}
).explain()

어떤 과정을 거쳐 쿼리가 진행되었는지 내역을 표시해준다.

 

여기서 우리가 쿼리 실행 속도를 위해 눈 여겨 볼 곳은 winningPlan이다.

winningPlan은 쿼리를 실행하는 가장 효율적인 계획을 말하며, 인덱스를 사용했다면 이 부분을 참고하여 인덱스를 사용했는지, 아닌지를 참고할 수 있다.

 

{
    "queryPlanner" : {
        "plannerVersion" : 1,
        "namespace" : "car_accident.by_type",
        "indexFilterSet" : false,
        "parsedQuery" : {
            .... 생략
        },
        "queryHash" : "638C6F98",
        "planCacheKey" : "638C6F98",
        
        // winningPlan을 참고해보자
        "winningPlan" : {
            "stage" : "PROJECTION_SIMPLE",
            "transformBy" : {
                "city_or_province" : 1.0,
                "county" : 1.0
            },
            "inputStage" : {
                "stage" : "COLLSCAN",
                "filter" : {
                    ... 생략
                },
                "direction" : "forward"
            }
        },
        "rejectedPlans" : []
    },
    "serverInfo" : {
        ... 서버에 대한 정보들
    },
    "ok" : 1.0
}

 

여기서 stage 부분을 차례대로 살펴보면 다음 순으로 되어 있다. 실행 순서는 역순이다.

"stage" : "PROJECTION_SIMPLE",

"stage" : "COLLSCAN",

 

위 쿼리를 수행하는 방법이 COLLSCAN => PROJECTION_SIMPLE 으로 이루어 졌다는 것이다.

이 말인 즉슨, 컬렉션 전체를 스캔 한 후 projection을 위해 필요한 필드만 남기고 다른 필드를 삭제했다는 것이다.

 

컬렉션 전체를 스캔했다는 것은 인덱스를 사용하지 않은 쿼리 탐색을 실행했다는 것이다.

인덱스가 사용되었다면 IXSCAN이 보여야 한다.

 

COLLSCAN : 컬렉션 전체 스캔

IXSCAN : 인덱스 키 스캔

FETCH : 도큐먼트를 불러온다

UPDATE : 도큐먼트를 수정한다

SORT : 도큐먼트를 정렬한다

PROJECTION : projection을 위해 일부 필드만 남기고 다른 필드를 삭제한다.

SHARD_MERGE : 샤딩되어 흩어진 정보를 모은다.

 

 

db.by_type.createIndex({accident_count: 1})
db.by_type.find(
  {type: "차대차", accident_count: {$gte: 100}, death_toll: 0},
  {city_or_province: 1, county: 1}
).explain()

 

위와 같이 인덱스를 설정한 후 explain을 해보았습니다. 이제는 IXSCAN이 뜨네요

"stage" : "FETCH",

"stage" : "IXSCAN",

 

이 말은, IXSCAN(인덱스를 살피고), FETCH(필요한 정보만 불러옴) 순으로 쿼리가 실행되었다는 것입니다.

 

{
    "queryPlanner" : {
        "winningPlan" : {
            "stage" : "PROJECTION_SIMPLE",
            "transformBy" : {
                "city_or_province" : 1.0,
                "county" : 1.0
            },
            "inputStage" : {
                "stage" : "FETCH",
                "filter" : {
                },
                "inputStage" : {
                    "stage" : "IXSCAN",
                    "keyPattern" : {
                        "accident_count" : 1.0
                    },
                    "indexName" : "accident_count_1", // 사용한 인덱스의 이름
                    "isMultiKey" : false, // multi key입니까?
                    "multiKeyPaths" : {
                        "accident_count" : []
                    },
                    "isUnique" : false, // unique 인덱스입니까?
                    "isSparse" : false, // sparse 인덱스입니까?
                    "isPartial" : false,
                    "indexVersion" : 2,
                    "direction" : "forward",
                    "indexBounds" : {
                        "accident_count" : [ 
                            "[100.0, inf.0]"
                        ]
                    }
                }
            }
        },
        "rejectedPlans" : []
    },
    "ok" : 1.0
}

 

 

하나의 쿼리 명령은 explain을 통해 그 내역을 살필 수 있지만 실제 서비스가 운영중이라면 프로파일러를 이용하는 것이 좋습니다.


darren, dev blog
블로그 이미지 DarrenKwonDev 님의 블로그
VISITOR 오늘 / 전체