MongoDB Index #.4 Multi key Index

이전 포스팅

 

멀티 키 인덱스 (Multi key Index)

MongoDB는 도큐먼트 기반의 비정규화된 데이터를 저장하는 데이터베이스입니다. 따라서 하나의 도큐먼트가 배열 형태의 데이터를 가지는 경우가 많이 발생하는데, 배열값이 있는 필드를 인덱싱하기 위해서 MongoDB는 각 엘리먼트에 대한 인덱스 키를 만드는데 이 것이 멀티 키 인덱스 입니다. 멀티 키 인덱스는 배열 필드에 대한 효율적인 쿼리를 지원하며, 스칼라 값 및 서브 도큐먼트 저장된 배열을 통해 구성됩니다. 하나의 도큐먼트에 여러 개의 인덱스 키를 가지는 형태이며, 반대로 보면 여러 인덱스키가 하나의 도큐먼트를 바라보는 구조를 의미합니다.

 

Index Bounds

인덱스 바운드란, 검색 조건을 통해 원하는 결과를 얻기 위해서 인덱스를 스캔 해야하는 범위를 나타내는데, 멀티 키 인덱스의 스캔 범위 결정 방식은 일반 인덱스와는 조금 다르게 결정됩니다.

두 개의 조건 [3, Infinity], [-Infinity, 6]이 주어지면 두 조건의 교집합은 [3,6]이 됩니다. 인덱스에서 교집합은 이렇게 두 개의 조건에서 공통된 부분의 논리적 결합(and)를 나타냅니다. 아래와 같은 두개의 도큐먼트가 있을때, BETWEEN  3 AND 6 조건을 실행해보겠습니다.

{ _id: 1, item: "ABC", ratings: [ 2, 9 ] }
{ _id: 2, item: "XYZ", ratings: [ 4, 3 ] }

그리고 멀티 키 인덱스를 생성해 줍니다.

> db.survey.createIndex( { ratings: 1 } )

그리고 결과 값을 조회합니다.

> db.survey.find( { ratings : { $gte: 3, $lte: 6 } } )
{ _id: 1, item: "ABC", ratings: [ 2, 9 ] } 
{ _id: 2, item: "XYZ", ratings: [ 4, 3 ] }

이런식으로 쿼리를 하는 경우 [2,9]를 가진 원치않는 결과값까지 출력하게 되는데 실제 MongoDB의 멀티키 인덱스는 RDBMS의 BETWEEN과는 다르게 동작하기 때문입니다. 각각의 조건 $gte, $lte를 따로 비교한 다음, 두 개의 결과 값을 병합하는 합집합 쿼리로 결과를 반환합니다.

따라서 배열의 경우 $elemMatch 연산자를 이용해야 합니다.

> db.survey.find( { ratings : { $elemMatch: { $gte: 3, $lte: 6 } } } )

{ _id: 2, item: "XYZ", ratings: [ 4, 3 ] }

$elemMatch연산자를 이용해야 배열 안에 있는 조건에 맞는 엘리먼트들의 교집합을 적용할 수 있습니다. 하지만, 이 것이 BETWEEN 3 AND 6 의 의미는 아닙니다. 실제로 다음과 같은 데이터가 추가 되었을때 동작을 살펴보겠습니다.

> db.survay.insert({ _id: 3, item: "MNQ", ratings: [ 2, 4 ] })

> db.survey.find( { ratings : { $elemMatch: { $gte: 3, $lte: 6 } } } ) 

{ _id: 2, item: "XYZ", ratings: [ 4, 3 ] }
{ _id: 3, item: "MNQ", ratings: [ 2, 4 ] }

3 ≤ X ≤ 6 의 조건에 일치하는 엘리먼트만으로 된 도큐먼트를 찾는것이 아닌, 배열의 엘리먼트 중 3 ≤ X ≤ 6의 조건에 일치하는 엘리먼트를 하나라도 가진 모든 도큐먼트를 호출하게 됩니다. 세번째 도큐먼트의 배열 값 중 4라는 값이 3 ≤ X ≤ 6의 조건을 만족하기 때문에 조건에 일치하지 않는 2가 포함되어 있어도 해당 도큐먼트를 반환하게 되는 것 입니다.

 

제약 조건

  • 멀티 키 인덱스는 샤드키로 사용할 수 없습니다.
  • 멀티 키 인덱스를 포함한 결합 인덱스(Compound Index)는 단 하나만의 필드와 결합 할 수 있습니다.
  • 해시 알고리즘을 사용하는 인덱스는 멀티 키 인덱스로 정의될 수 없습니다.
  • 멀티 키 인덱스는 커버링 인덱스 처리가 불가능합니다.

 

배열의 인덱싱 처리에서 중요한것은 배열의 엘리먼트들이 늘어날수록 쌓인 디스크 데이터가 MongoDB의 캐시 메모리보다 커지기 시작하면 Insert의 영향도가 눈에 보이게 나타나기 시작합니다. 이런 현상은 대량으로 데이터를 적재하거나 삭제할 때도 나타날 수 있으며, 인덱스를 리빌드할 때에도 멀티 키 인덱스라면 더 오랜 시간이 걸릴수 있음을 상기해야합니다.

 

멀티 키 인덱스의 정렬

MongoDB 3.6에서 배열 필드에 대한 정렬 동작이 변경되었는데, 멀티키 인덱스로 인덱싱된 배열에서 정렬 할 때 쿼리 계획에 blocking SORT 스테이지가 포함됩니다. 새로운 정렬 동작은 성능에 부정적인 영향을 미칠 수도 있습니다.  Blocking SORT에서 모든 입력은 결과값을 반환하기 전에 정렬 단계에서 이뤄져야 합니다. non-blocking SORT 또는 indexed SORT에서의 정렬 단계는 인덱스를 스캔하여 요청 된 순서로 결과를 생성합니다.

 

샤드 키, 해쉬 인덱스

멀티 키 인덱스는 샤드 키 또는 해쉬 인덱스로 지정할 수 없습니다.

 

배열 필드 전체에 대한 쿼리

MongoDB는 멀티 키 인덱스를 사용하면 쿼리 배열의 첫번째 엘리먼트를 조회할 수는 있지만,쿼리 필터가 배열이 가진 엘리먼트 전체가 정확히 일치하는것을 지정하여 배열 전체가 동일한 값을 가지 결과를 찾을 수는 없습니다. 대신 멀티 키 인덱스를 사용하여 배열의 첫번째 엘리먼트를 조회 한 후 MongoDB의 배열과 일치하는 배열을 가진 도큐먼트를 검색하고 쿼리와 일치한 배열을 가진 도큐먼트에서 필터링 합니다.

 

$expr

멀티 키 인덱스는 $expr을 지원하지 않습니다.

 

다음 포스트

 

참고 자료

도서: Real MongoDB

MongoDB Manual: https://docs.mongodb.com/manual/

You may also like...

1 Response

  1. 2021년 4월 28일

    […] look 이렇게 두 단어를 인덱스에 저장하는 방식이 Text Index 입니다.Text Index는 멀티 키 인덱스와 동일한 자료 구조로 저장이 […]

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 항목은 *(으)로 표시합니다