MongoDB Index #.4 Multi key Index
이전 포스팅
MongoDB Index#.3 Hash Index
MongoDB Index#.3 Hash Index 이전 포스팅 MongoDB의 Hash Index 해시 인덱스는 B-Tree 만큼 범용적이지는 않지만 고유의 특성과 용도를 지닌 인덱스 중에 하나로, 주어진 키 값을 이용하여 목표 레코드의 주소를 직접적으로 계산하는 방식입니다. 따라서 단일…
멀티 키 인덱스 (Multi key Index)
MongoDB는 도큐먼트 기반의 비정규화된 데이터를 저장하는 데이터베이스입니다. 하나의 도큐먼트가 배열 형태의 데이터를 가지는 경우가 많은데, 배열 필드를 인덱싱할 때 MongoDB는 각 엘리먼트에 대한 인덱스 키를 자동으로 만듭니다. 이것이 멀티 키 인덱스입니다.
멀티 키 인덱스는 명시적으로 선언하는 것이 아니라, 배열 값이 포함된 필드에 인덱스를 생성하면 MongoDB가 자동으로 멀티 키 인덱스로 만듭니다. 배열의 각 고유 값마다 하나의 인덱스 엔트리가 생성되며, 스칼라 값 및 서브 도큐먼트가 저장된 배열을 통해 구성됩니다. 하나의 도큐먼트에 여러 개의 인덱스 키를 가지는 형태이며, 반대로 보면 여러 인덱스 키가 하나의 도큐먼트를 바라보는 구조입니다.
db.movies.createIndex( { genres: 1 } )
위 명령으로 genres 필드에 인덱스를 생성하면, genres가 배열이므로 자동으로 멀티 키 인덱스가 됩니다.
Index Bounds
인덱스 바운드란 검색 조건을 통해 인덱스를 스캔해야 하는 범위를 나타냅니다. 멀티 키 인덱스의 스캔 범위 결정 방식은 일반 인덱스와는 다르게 동작합니다.
일반적으로 두 개의 조건 [3, Infinity], [-Infinity, 6]이 주어지면 두 조건의 교집합은 [3,6]이 됩니다. 그러나 멀티 키 인덱스에서는 특별한 규칙이 적용됩니다. 다음 두 개의 도큐먼트를 예로 들어봅니다.
db.survey.insertMany([
{ _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]를 가진 원치 않는 결과까지 출력됩니다. 멀티 키 인덱스는 RDBMS의 BETWEEN과는 다르게 동작하기 때문입니다. $elemMatch 없이 여러 조건을 지정하면, MongoDB는 각 조건에 대해 인덱스 바운드를 따로 계산한 뒤 그 중 하나를 임의로 선택하여 스캔하고, 나머지 조건은 필터로 적용합니다. 어떤 바운드를 선택할지는 보장되지 않습니다.
배열의 경우 $elemMatch 연산자를 이용해야 교집합 바운드를 적용할 수 있습니다.
db.survey.find( { ratings : { $elemMatch: { $gte: 3, $lte: 6 } } } )
결과:
{ _id: 2, item: "XYZ", ratings: [ 4, 3 ] }
$elemMatch 연산자는 하나의 배열 엘리먼트가 모든 조건을 만족하는 도큐먼트를 찾도록 하며, 인덱스 바운드도 교집합 [3, 6]으로 좁아집니다. 다음 데이터를 추가해봅니다.
db.survey.insertOne({ _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가 포함되어 있어도 해당 도큐먼트를 반환합니다.
제약 조건
복합 멀티 키 인덱스
복합 인덱스에서 멀티 키 인덱스를 사용할 때는 중요한 제약이 있습니다. 복합 멀티 키 인덱스에서 각 인덱싱된 도큐먼트는 최대 하나의 필드만 배열 값을 가질 수 있습니다.
- 인덱스 스펙에서 두 개 이상의 필드가 배열이면 복합 멀티 키 인덱스를 생성할 수 없습니다.
- 복합 멀티 키 인덱스가 이미 존재하는 경우, 이 제약을 위반하는 도큐먼트를 삽입하면 삽입이 실패합니다.
예를 들어 { genres: 1, year: 1 } 복합 인덱스는 genres와 year 중 하나만 배열이면 허용되지만, 둘 다 배열인 도큐먼트를 삽입하려고 하면 에러가 발생합니다.
커버드 쿼리
멀티 키 인덱스는 매우 제한적으로만 커버드 쿼리를 지원합니다. 다음 조건을 모두 만족해야 합니다:
- 쿼리가 배열 필드를 반환하지 않아야 합니다 (프로젝션에 배열 필드가 없어야 함)
- 멀티 키 인덱스가 복합 인덱스여야 합니다
- 쿼리가
$elemMatch를 포함하지 않아야 합니다
샤드 키
멀티 키 인덱스는 샤드 키 인덱스로 사용할 수 없습니다. 단, 샤드 키 인덱스가 복합 인덱스의 접두어(prefix)인 경우, 뒤따르는 필드가 배열이면 그 복합 인덱스는 멀티 키가 될 수 있습니다.
해시 인덱스
해시 인덱스는 멀티 키 인덱스가 될 수 없습니다.
인덱스 키 생성 제한
하나의 도큐먼트에서 생성되는 인덱스 키의 최대 개수는 indexMaxNumGeneratedKeysPerDocument 파라미터로 제한됩니다. 기본값은 100,000개입니다. 이 제한을 초과하는 작업은 실패하며, out-of-memory 에러를 방지하기 위한 것입니다. 이 파라미터는 조정 가능하지만, 배열의 엘리먼트가 매우 많거나 복합 인덱스에서 여러 배열 필드의 카티지언 곱이 계산될 때 이 제한에 도달할 수 있습니다.
기타 제약
$expr연산자는 멀티 키 인덱스를 지원하지 않습니다.- 배열 필드에 대한 정렬 시, 특정 조건을 만족하지 않으면 쿼리 계획에 in-memory sort 스테이지가 포함됩니다.
정렬과 성능
멀티 키 인덱스로 인덱싱된 배열 필드에서 정렬할 때, 다음 조건을 모두 만족하지 않으면 쿼리 계획에 blocking SORT 스테이지가 포함됩니다:
- 모든 정렬 필드의 인덱스 바운더리가
[MinKey, MaxKey]여야 합니다 - 멀티 키 인덱스 필드의 바운더리가 정렬 패턴과 동일한 경로 접두어를 가지지 않아야 합니다
Blocking SORT는 모든 입력을 메모리에서 정렬한 후 결과를 반환하므로, 인덱스를 직접 스캔하여 정렬된 결과를 생성하는 indexed SORT보다 성능이 떨어질 수 있습니다.
배열 전체 매치 쿼리
멀티 키 인덱스를 사용하여 배열 전체가 정확히 일치하는 도큐먼트를 찾을 때(예: { genres: ["Drama"] }), MongoDB는 인덱스를 사용하여 배열의 첫 번째 엘리먼트를 포함하는 도큐먼트를 먼저 찾은 다음, 인덱스만으로는 전체 배열 일치를 확인할 수 없으므로 후보 도큐먼트들을 가져와 배열 전체가 정확히 일치하는지 필터링합니다.
다음 포스트
MongoDB Index #.5 Full Text Search Index
이전 포스트 Full Text Search Index (전문 검색 인덱스) DBMS에서 일반적으로 전문 검색 엔진을 구축할 때 사용하는 알고리즘은 크게 두 가지로 나눌 수 있습니다. 하나는 형태소 분석(어근 분석, stemming)과 N-Gram 2가지로 나뉘어집니다. 명사와 조사 사이를 띄어쓰기를…
참고 자료
도서: Real MongoDB
MongoDB Manual: https://docs.mongodb.com/manual/