[MongoDB] 강좌 6편 Index 설정


이번 강좌는 MongoDB의 Index에 관한 내용입니다.

Index란?

Index는 MongoDB에서 데이터 쿼리를 더욱 효율적으로 할 수 있게 해줍니다. 인덱스가 없이는, MongoDB는 collection scan – 컬렉션의 데이터를 하나하나 조회 – 방식으로 스캔을 하게 됩니다. 만약 document의 갯수가 매우 많다면, 많은 만큼 속도가 느려지겠죠? 이 부분을 향상시키기 위하여 인덱스를 사용하면 더 적은 횟수의 조회로 원하는 데이터를 찾을 수 있습니다.

Document의 필드(들) 에 index 를 걸면, 데이터의 설정한 키 값을 가지고 document들을 가르키는 포인터값으로 이뤄진 B-Tree를 만듭니다. 여기서 B-Tree는 Balanced Binary search Tree 인데요, B-Tree 에서 Binary Search를 통하여 쿼리 속도를 매우 빠르게 향상 시킬 수 있습니다. Balanced Binary Tree / Binary Search 키워드에 대해선 자료구조를 공부하신 분들이라면 익숙하겠지만 그렇지 않은 분들을 위해 한번 간단하게 원리를 설명해보겠습니다.

만약에 숫자가 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 가 있을 때 이 중에서 14를 찾는다고 한다면,  Index가 없을때는 1부터 14까지 쭉 조회를 하여 14를 찾아냅니다.

하지만 Index가 있을땐 다음과 같은 B-Tree를 만듭니다. ( B-Tree가 만들어지는 과정은 생략하겠습니다. )

Untitled-7

이 트리를 간단하게 설명해드리자면, 특정 값보다 작은 값은 왼쪽에, 큰 값은 오른쪽에 위치하는 규칙을 가지고 있는데요.

이 트리를 사용하여 14를 찾을땐 8 → 12 → 14 이렇게 3번의 조회만에 14를 찾을 수 있게됩니다.

그리고, 순회 알고리즘을 사용하여 가장 낮은값부터 가장 큰 값 까지 매우 효율적으로 정렬도 할 수 있습니다.bintree-traverse

자, 이제 원리를 이해하였다면 MongoDB에서 인덱스를 사용하는 방법을 배워보겠습니다.

Index의 종류

기본 인덱스 _id

모든 MongoDB의 컬렉션은 기본적으로 _id 필드에 인덱스가 존재합니다. 만약에 컬렉션을 만들 때  _id 필드를 따로 지정하지 않으면 mongod드라이버가 자동으로 _id 필드 값을 ObjectId로 설정해줍니다.

_id 인덱스는 unique(유일)하고 이는 MongoDB 클라이언트가 같은 _id 를 가진 문서를 중복적으로 추가하는 것을 방지합니다.

Single(단일) 필드 인덱스

MongoDB 드라이버가 지정하는 _id 인덱스 외에도, 사용자가 지정 할 수 있는 단일 필드 인덱스가 있습니다.

다음 이미지 처럼 score 값으로 정렬 할 수 있지요.

Diagram of an index on the ``score`` field (ascending).

Compound (복합)  필드 인덱스

두개 이상의 필드를 사용하는 인덱스를 복합 인덱스라고 부릅니다. 다음 이미지와 같이 첫번째 필드 (userid)는 오름차순으로, 두번째 필드 (score)는 내림차순으로 정렬 해야 하는 상황이 있을때 사용합니다.

Diagram of a compound index on the ``userid`` field (ascending) and the ``score`` field (descending). The index sorts first by the ``userid`` field and then by the ``score`` field.

Multikey 인덱스

필드 타입이 배열인 필드에 인덱스를 적용 할 때는 Multikey 인덱스가 사용됩니다. 이 인덱스를 통하여 배열에 특정 값이 포함되어 있는 document를 효율적으로 스캔 할 수 있습니다.Diagram of a multikey index on the ``addr.zip`` field. The ``addr`` field contains an array of address documents. The address documents contain the ``zip`` field.

Geospatial(공간적) Index

지도의 좌표와 같은 데이터를 효율적으로 쿼리하기 위해서 (예: 특정 좌표 반경 x 에 해당되는 데이터를 찾을 때) 사용되는 인덱스입니다. 자세한 사항은 매뉴얼을 참고해주세요.

Text 인덱스

텍스트 관련 데이터를 효율적으로 쿼리하기 위한 인덱스입니다. 자세한 사항은 매뉴얼 Text Indexes 을 참고하세요.

해쉬 (hashed) 인덱스

이 인덱스를 사용하면 B Tree가아닌 Hash 자료구조를 사용합니다. Hash는 검색 효율이 B Tree보다 좋지만, 정렬을 하지 않습니다.

 인덱스 생성

인덱스를 생성 할 땐, 다음과 같은 createIndex() 메소드를 사용합니다. 파라미터는 인덱스를 적용할 필드를 전달합니다. 값을 1로하면 오름차순으로, -1로 하면 내림차순으로 정렬합니다.

db.COLLECTION.createIndex( { KEY: 1 } )

다양한 예제를 통하여 인덱스 생성을 배워보도록 하겠습니다.

예제1 단일 필드 인덱스 생성

db.report.createIndex( { score: 1 } )

score 필드에 인덱스를 걸어줍니다.

이 인덱스는 다음과 같은 쿼리를 할 때 효율적으로 실행하게 해줍니다.

db.report.find( { score: 57 } )
db.report.find( { score: { $gt: 60 } } )

예제2 복합 필드 인덱스 생성

db.report.createIndex( { age: 1, score: -1} )

이렇게 여러 필드를 넣어 인덱스를 생성하면 age를 오름차순으로 정렬한 상태에서 score 는 내림차순으로 정렬합니다.

 인덱스 속성

인덱스에 속성을 추가 할 땐 createIndex() 메소드의 두번째 인자에 속성값을 document 타입으로 전달해주면 됩니다.

db.COLLECTION.createIndex( { KEY: 1 }, {  PROPERTY: true } )

인덱스에 적용 할 수 있는 4가지 속성이 있는데요, 한번 이에 대해 알아보고, 예제를 통해 사용법을 배워보도록 하겠습니다.

Unique (유일함) 속성

_id 필드처럼 컬렉션에 단 한개의 값만 존재 할 수 있는 속성입니다.

예제3 email 인덱스에 unique 속성 적용

db.userinfo.createIndex( { email: 1 }, { unique: true } )

unique 속성은 다음처럼 복합 인덱스에도 적용 할 수 있습니다.

예제4 firstName 과 lastName 복합인덱스에 unique 속성 적용

db.userinfo.createIndex( { firstName: 1, lastName: 1 }, { unique: true } )

Partial (부분적) 속성

partial 속성은 document의 조건을 정하여 일부 document에만 인덱스를 적용 할 때 사용됩니다.

partial 속성을 사용하면, 필요한 부분에만 인덱싱을 사용하여 저장공간도 아끼고 속도를 더 높일수 있습니다.

예제5 visitors 값이 1000 보다 높은 document에만 name 필드에 인덱스 적용

db.store.createIndex( 
   { name: 1 }, 
   { partialFilterExpression: { visitors: { $gt: 1000 } } }
)

예제6 TLL 속성

이 인덱스 속성은 Date 타입, 혹은 Date 배열 타입의 필드에 적용 할 수 있는 속성입니다. 이 속성을 사용하여 document를 expire(만료) 시킬 수 있습니다. 즉, 추가하고 특정 시간이 지나면, document 를 컬렉션에서 제거합니다.

예제: notifiedDate 가 현재 시각과 1시간 이상 차이나면 제거

db.notifications.createIndex( { "notifiedDate": 1 }, { expireAfterSeconds: 3600 } )

 

document가 만료되어 제거 될 때, 시간이 아주 정확하지는 않습니다. 만료되는 document를 제거하는 thread는 매 60초마다 실행됩니다. 이점 유의해주세요.

인덱스 조회

생성된 인덱스를 조회 할 땐 getIndexes() 메소드를 사용합니다.

db.COLLECTION.getIndexes()

 

인덱스 제거

인덱스를 제거 할 땐 dropIndex() 메소드를 사용합니다.

db.COLLECTION.dropIndex( { KEY: 1 } )

 

( _id 인덱스를 제외한 ) 모든 인덱스를 제거 할 땐 dropIndexes() 메소드를 사용합니다.

 

마무리하며..

강좌 6편을 끝으로 MongoDB 기초 연재 강좌를 마치겠습니다.  다음 강좌부터는 팁 위주로 올리도록 하겠습니다. 감사합니다.

  • 고영석

    강좌 잘 보았습니다.
    많은 도움이 되었네요.

    • 도움이 되었다니 영광이에요 🙂

  • GT M

    인덱스가 정확히 뭘까요 .. ?

    • 일종의 순서번호 라고 이해를 해놓으면 되겠습니다. 어떠한 조건으로 미리 정렬을 해놓고 각 데이터마다 순서번호를 줘서 찾기 더 편하게 해주는거죠.

      • GT M

        정렬만하는게 아니라 정렬을 하면서 키값?을 주는건가요?

        • index 의 뜻을 그대로 해석 했을 때 순서 번호 같은거지, 사실상 몇번째 아이템은 어떤걸 가르키고..그렇게 키를 따로 설정을 하진 않습니다. 제 설명만 읽으면 좀 오해를 할 수도 있겠군요..

          다만, B-tree 의 자료구조로 데이터를 담게 됩니다.

          • GT M

            그렇군요 .. 뭔가 이해갈듯하면서 헷갈리네요

  • 이인용

    강좌 잘 읽었습니다! 읽다보니 잘못된 내용이 있어서 말씀드립니다. index에 사용되는 B-Tree는 이진 트리(Binary Tree)가 아니에요. https://ko.wikipedia.org/wiki/B_%ED%8A%B8%EB%A6%AC

    • 오호… 제가 잘못알고있었군요. 감사합니다.
      빠른 시일내에 수정할게요 🙂

  • moon

    인덱스를 생성만 해놓으면 다음부터 find하면 그 인덱스에 기초해서 find해주는건가요? 대단허네요..

  • Coding Mentor

    벨로퍼트님 궁금한게 세가지 있는데요

    1.

    이미 컬렉션에 다큐먼트를 추가할때 이미 몽고디비가 알아서 _id라는 필드에 인덱스를 생성해놨는데

    또 createIndex() 로 인덱스를 만들어줘야한다는게 이해가 안됩니다.

    2.
    아 그리고 어찌됬건간에 사용자가 따로 인덱스를 만들어주면 쿼리할때 더 빨라지긴 하는거겠죠?

    3.쿼리를 할때 인덱스가 생성된 필드를 쿼리했을때만 성능이 빨라지나요? 아니면 아무 필드에나 일단 인덱스를 하나만 만들어놓으면 쿼리속도가 빨라지나요?

    예를들어 필드 a, b, c중에 a에다가만 인덱스를 생성했는데 쿼리문이

    find(b : {$gt: 42} }

    여서 인덱스가 없는 b를 쿼리하는거면 인덱스의 진가를 맛볼수는 없는건가요?