문서 메뉴

문서 홈애플리케이션 개발MongoDB 매뉴얼

인덱스를 사용하여 쿼리 결과 정렬하기

이 페이지의 내용

  • 단일 필드 인덱스로 정렬
  • 여러 필드에서 정렬
  • 인덱스 정렬 순서
  • 인덱스 사용 및 데이터 정렬
  • 예제

인덱스에는 정렬된 기록이 포함되므로 MongoDB는 정렬 필드를 포함하는 인덱스에서 정렬 결과를 얻을 수 있습니다. 정렬이 쿼리 조건자와 동일한 인덱스를 사용하는 경우 MongoDB는 정렬 작업을 지원하기 위해 여러 인덱스를 사용할 수 있습니다.

MongoDB가 인덱스 또는 인덱스를 사용하여 정렬을 순서대로 가져올 수 없는 경우, MongoDB는 데이터에 대해 블로킹 정렬 작업을 수행해야 합니다. 블로킹 정렬은 MongoDB가 정렬에 대한 모든 입력 문서를 소비하고 처리해야 함을 나타냅니다.블로킹 정렬은 컬렉션이나 데이터베이스에서 동시 작업을 차단하지 않습니다.

MongoDB 6 부터 시작.0, 서버가 파이프라인 실행 단계에 100 메가바이트 이상의 메모리를 필요로 하는 경우 MongoDB는 쿼리에 { allowDiskUse: false } 를 지정하지 않는 한 임시 파일을 디스크에 자동으로 씁니다. 서버에 블로킹 정렬 작업을 위해 100 메가바이트 이상의 시스템 메모리가 필요한 경우, 쿼리가 cursor.allowDiskUse() 을(를) 지정하지 않는 한 MongoDB는 오류를 반환합니다. 자세한 내용은 allowDiskUseByDefault 를 참조하세요.

인덱스를 사용하는 정렬 작업은 블로킹 정렬보다 성능이 더 좋은 경우가 많습니다.

참고

멀티키 인덱스로 인덱싱된 배열 필드를 기준으로 정렬하는 경우 다음 두 가지 모두에 해당하지 않는 한 쿼리 계획에 블로킹 정렬 단계가 포함됩니다.

  • 모든 정렬 필드의 인덱스 경계[MinKey, MaxKey]입니다.

  • 멀티키 인덱스 필드의 경계에는 정렬 패턴과 동일한 경로 접두사가 없습니다.

단일 필드에 오름차순 또는 내림차순 인덱스가 있는 경우 정렬 필드 작업은 어느 방향으로든 이루어질 수 있습니다.

예를 들어, records 컬렉션의 a 필드에 오름차순 인덱스를 생성합니다.

db.records.createIndex( { a: 1 } )

이 인덱스는 다음에서 a의 오름차순 정렬을 지원할 수 있습니다.

db.records.find().sort( { a: 1 } )

인덱스를 역순으로 탐색하여 에서 다음과 같이 a에 대한 내림차순 정렬을 지원할 수도 있습니다.

db.records.find().sort( { a: -1 } )

복합 인덱스를 생성하여 여러 필드에서 정렬을 지원합니다.

인덱스의 모든 키 또는 서브세트에 대해 정렬을 지정할 수 있지만, 정렬 키는 인덱스에 표시되는 것과 동일한 순서 로 나열되어야 합니다. 예를 들어, 인덱스 키 패턴 { a: 1, b: 1 }{ a: 1, b: 1 }의 정렬을 지원할 수 있지만 { b: 1, a: 1 }의 정렬은 지원할 수 없습니다.

쿼리가 정렬에 복합 인덱스를 사용하려면 cursor.sort() 문서의 모든 키에 지정된 정렬 방향이 인덱스 키 패턴 또는 그 역과 일치해야 합니다. 예를 들어 인덱스 키 패턴 { a: 1, b: -1 }{ a: 1, b: -1 }{ a: -1, b: 1 }의 정렬을 지원할 수 있지만 { a: -1, b: -1 } 또는 {a: 1, b: 1}의 정렬은 지원하지 않습니다.

정렬 키가 인덱스 키 또는 인덱스 접두사에 해당하는 경우, MongoDB는 인덱스를 사용하여 쿼리 결과를 정렬할 수 있습니다. 복합 인덱스의 접두사는 인덱스 키 패턴의 시작 부분에 있는 하나 이상의 키로 구성된 하위 집합입니다.

예를 들어 data 컬렉션에 복합 인덱스를 생성합니다.

db.data.createIndex( { a:1, b: 1, c: 1, d: 1 } )

그러면 아래와 같은 항목이 인덱스의 접두사에 해당합니다.

{ a: 1 }
{ a: 1, b: 1 }
{ a: 1, b: 1, c: 1 }

다음 쿼리 및 정렬 작업은 인덱스 접두사를 사용하여 결과를 정렬합니다. 이러한 작업은 메모리에 있는 결과 세트를 정렬할 필요가 없습니다.

예제
인덱스 접두사
db.data.find().sort( { a: 1 } )
{ a: 1 }
db.data.find().sort( { a: -1 } )
{ a: 1 }
db.data.find().sort( { a: 1, b: 1 } )
{ a: 1, b: 1 }
db.data.find().sort( { a: -1, b: -1 } )
{ a: 1, b: 1 }
db.data.find().sort( { a: 1, b: 1, c: 1 } )
{ a: 1, b: 1, c: 1 }
db.data.find( { a: { $gt: 4 } } ).sort( { a: 1, b: 1 } )
{ a: 1, b: 1 }

인덱스의 접두사 키가 쿼리 술어와 정렬 모두에 나타나는 다음 예제를 살펴보세요.

db.data.find( { a: { $gt: 4 } } ).sort( { a: 1, b: 1 } )

이러한 경우 MongoDB는 인덱스를 사용하여 정렬에서 지정한 순서대로 문서를 조회할 수 있습니다. 예제에서 볼 수 있듯이 쿼리 술어의 인덱스 접두사는 정렬의 접두사와 다를 수 있습니다.

인덱스는 접두사가 아닌 인덱스 키 패턴 하위 집합의 정렬 작업을 지원할 수 있습니다. 이렇게 하려면 정렬 키 앞에 오는 모든 접두사 키에 대한 동등성 조건이 쿼리에 포함되어야 합니다.

예를 들어 data 컬렉션의 인덱스는 다음과 같습니다.

{ a: 1, b: 1, c: 1, d: 1 }

다음 작업에서는 인덱스를 사용하여 정렬 순서를 확보할 수 있습니다.

예제
인덱스 접두사
db.data.find( { a: 5 } ).sort( { b: 1, c: 1 } )
{ a: 1 , b: 1, c: 1 }
db.data.find( { b: 3, a: 4 } ).sort( { c: 1 } )
{ a: 1, b: 1, c: 1 }
db.data.find( { a: 5, b: { $lt: 3} } ).sort( { b: 1 } )
{ a: 1, b: 1 }

마지막 작업에서 볼 수 있듯이 쿼리 문서에서 정렬 하위 집합 앞에 있는 인덱스 필드에만 동등성 조건이 적용되어야 합니다. 다른 인덱스 필드는 다른 조건을 지정할 수 있습니다.

쿼리가 정렬 사양 앞에 오거나 겹치는 인덱스 접두사에 동등성 조건을 지정하지 않으면 작업에서 인덱스를 효율적으로 사용할 수 없습니다. 예를 들어 다음 작업은 { c: 1 }의 정렬 문서를 지정하지만 쿼리 문서는 선행 인덱스 필드 ab의 동등성 일치를 포함하지 않습니다.

db.data.find( { a: { $gt: 2 } } ).sort( { c: 1 } )
db.data.find( { c: 5 } ).sort( { c: 1 } )

이러한 작업은 { a: 1, b: 1, c: 1, d: 1 } 인덱스를 효율적으로 사용할 수 없으며 문서를 조회하는 데 인덱스를 사용하지 않을 수도 있습니다.

인덱스된 문서 모음은 키 필드에 여러 데이터 유형을 가질 수 있습니다.

  • 인덱스에 여러 데이터 유형을 가진 키가 있는 경우 인덱스는 BSON 유형 정렬 순서에 따라 정렬됩니다.

  • 배열 비교에서

    • 보다 작음 비교 또는 오름차순 정렬은 배열의 가장 작은 요소를 BSON 유형 정렬 순서에 따라 비교합니다.

    • 보다 큼 비교 또는 내림차순 정렬은 배열에서 가장 큰 요소를 역 BSON 유형 정렬 순서에 따라 비교합니다.

    • 값이 하나의 요소 배열(예: [ 1 ])인 필드를 배열이 아닌 필드(예: 2)와 비교할 때 비교 대상은 12입니다.

    • 빈 배열(예: [ ])을 비교하면 빈 배열을 null 값보다 작거나 누락된 필드 값으로 간주합니다.

인덱스 정렬 예시를 참조하세요.

문자열 비교에 인덱스를 사용하려면 작업에서 동일한 데이터 정렬도 지정해야 합니다. 즉, 작업에서 다른 데이터 정렬을 지정하는 경우 데이터 정렬이 있는 인덱스는 인덱싱된 필드에서 문자열 비교를 수행하는 작업을 지원할 수 없습니다.

경고

데이터 정렬로 구성된 인덱스는 ICU 데이터 정렬 키를 사용하여 정렬 순서를 지정하므로, 데이터 정렬이 없는 인덱스의 경우 데이터 정렬 인식 인덱스 키가 인덱스 키보다 클 수 있습니다.

예를 들어, 컬렉션 myColl에는 대조 로케일이 "fr" 인 문자열 필드 category에 대한 색인이 있습니다.

db.myColl.createIndex( { category: 1 }, { collation: { locale: "fr" } } )

인덱스와 동일한 데이터 정렬을 지정하는 다음 쿼리 작업에서는 인덱스를 사용할 수 있습니다.

db.myColl.find( { category: "cafe" } ).collation( { locale: "fr" } )

하지만 기본적으로 '단순' 바이너리 데이터 정렬기를 사용하는 다음 쿼리 작업에서는 인덱스를 사용할 수 없습니다.

db.myColl.find( { category: "cafe" } )

인덱스 접두사 키가 문자열, 배열 및 내장된 문서가 아닌 복합 인덱스의 경우, 다른 데이터 정렬을 지정하는 작업에서도 인덱스를 사용하여 인덱스 접두사 키에 대한 비교를 지원할 수 있습니다.

예를 들어, 컬렉션 myColl에는 숫자 필드 scoreprice와 문자열 필드 category에 복합 인덱스가 있습니다. 인덱스는 문자열 비교를 위해 데이터 정렬 로캘 "fr"를 사용하여 만들어집니다.

db.myColl.createIndex(
{ score: 1, price: 1, category: 1 },
{ collation: { locale: "fr" } } )

문자열 비교에 "simple" 이진 데이터 정렬을 사용하는 다음 작업에서는 인덱스를 사용할 수 있습니다.

db.myColl.find( { score: 5 } ).sort( { price: 1 } )
db.myColl.find( { score: 5, price: { $gt: NumberDecimal( "10" ) } } ).sort( { price: 1 } )

색인이 생성된 category 필드의 문자열 비교를 위해 "simple" 이진 데이터 정렬을 사용하는 다음 작업은 색인을 사용하여 쿼리의 score: 5 부분만 수행할 수 있습니다.

db.myColl.find( { score: 5, category: "cafe" } )

중요

내장된 문서 키를 비롯한 문서 키와의 일치는 단순 이진 비교를 사용합니다. 즉, "foo.bár"와 같은 키에 대한 쿼리는 "foo.bar" 키와 일치하지 않습니다. 이는 강도 매개변수에 설정한 값에 관계없이 적용됩니다.

다음 예제는 인덱스 키의 유형이 동일하거나 다른 경우의 정렬을 보여줍니다.

keyTypes 컬렉션을 생성합니다.

db.keyTypes.insertMany( [
{ seqNum: 1, seqType: null, type: "null" },
{ seqNum: 29, seqType: null, type: "null" },
{ seqNum: 2, seqType: Int32("10"), type: "Int32" },
{ seqNum: 28, seqType: Int32("10"), type: "Int32" },
{ seqNum: 3, seqType: Long("10"), type: "Long" },
{ seqNum: 27, seqType: Long("10"), type: "Long" },
{ seqNum: 4, seqType: Decimal128("10"), type: "Decimal128" },
{ seqNum: 26, seqType: Decimal128("10"), type: "Decimal128" },
{ seqNum: 5, seqType: Double("10"), type: "Double" },
{ seqNum: 25, seqType: Double("10"), type: "Double" },
{ seqNum: 6, seqType: String("10"), type: "String" },
{ seqNum: 24, seqType: String("10"), type: "String" },
{ seqNum: 7, seqType: [ "1", "2", "3" ], type: "Array" },
{ seqNum: 23, seqType: [ "1", "2", "3" ], type: "Array" },
{ seqNum: 8, seqType: [ [1], [2], [3] ], type: "Array" },
{ seqNum: 22, seqType: [ [1], [2], [3] ], type: "Array " },
{ seqNum: 9, seqType: [ 1, 2, 3 ], type: "Array" },
{ seqNum: 21, seqType: [ 1, 2, 3 ], type: "Array" },
{ seqNum: 10, seqType: true, type: "Boolean" },
{ seqNum: 11, seqType: new Timestamp(), type: "Timestamp" },
{ seqNum: 12, seqType: new Date(), type: "Date" },
{ seqNum: 13, seqType: new ObjectId(), type: "ObjectId" },
] )

시퀀스 번호(seqNum) 및 유형(seqType) 필드에 인덱스를 생성합니다.

db.keyTypes.createIndex( { seqNum: 1 } )
db.keyTypes.createIndex( { seqType: 1 } )

find()를 사용하여 컬렉션을 쿼리합니다. 프로젝션 문서{ _id: 0 }는 출력 디스플레이에서 _id 필드를 표시하지 않습니다.

db.keyTypes.find( {}, { _id: 0 } )

문서가 삽입된 순서대로 반환됩니다.

{ seqNum: 1, seqType: null, type: 'null' },
{ seqNum: 29, seqType: null, type: 'null' },
{ seqNum: 2, seqType: 10, type: 'Int32' },
{ seqNum: 28, seqType: 10, type: 'Int32' },
{ seqNum: 3, seqType: Long("10"), type: 'Long' },
{ seqNum: 27, seqType: Long("10"), type: 'Long' },
{ seqNum: 4, seqType: Decimal128("10"), type: 'Decimal128' },
// Output truncated

시퀀스 번호(seqNum) 인덱스는 동일한 유형의 값을 가집니다. seqNum 인덱스를 사용하여 keyTypes 컬렉션을 쿼리합니다.

db.keyTypes.find( {}, { _id: 0 } ).sort( { seqNum: 1} )

seqNum 키는 정수입니다. 문서는 번호 순서대로 반환됩니다.

{ seqNum: 1, seqType: null, type: 'null' },
{ seqNum: 2, seqType: 10, type: 'Int32' },
{ seqNum: 3, seqType: Long("10"), type: 'Long' },
{ seqNum: 4, seqType: Decimal128("10"), type: 'Decimal128' },
{ seqNum: 5, seqType: 10, type: 'Double' },
{ seqNum: 6, seqType: '10', type: 'String' },
{ seqNum: 7, seqType: [ '1', '2', '3' ], type: 'Array' },
// Output truncated

시퀀스 유형(seqType) 인덱스는 다른 유형의 값을 가집니다. seqType 인덱스를 사용하여 keyTypes 컬렉션을 쿼리합니다.

db.keyTypes.find( {}, { _id: 0 } ).sort( { seqType: 1} )

문서는 BSON 유형 정렬 순서로 반환됩니다.

{ seqNum: 1, seqType: null, type: 'null' },
{ seqNum: 29, seqType: null, type: 'null' },
{ seqNum: 9, seqType: [ 1, 2, 3 ], type: 'Array' },
{ seqNum: 21, seqType: [ 1, 2, 3 ], type: 'Array' },
{ seqNum: 2, seqType: 10, type: 'Int32' },
{ seqNum: 28, seqType: 10, type: 'Int32' },
{ seqNum: 3, seqType: Long("10"), type: 'Long' },
{ seqNum: 27, seqType: Long("10"), type: 'Long' },
{ seqNum: 4, seqType: Decimal128("10"), type: 'Decimal128' },
{ seqNum: 26, seqType: Decimal128("10"), type: 'Decimal128' },
{ seqNum: 5, seqType: 10, type: 'Double' },
{ seqNum: 25, seqType: 10, type: 'Double' },
{ seqNum: 7, seqType: [ '1', '2', '3' ], type: 'Array' },
{ seqNum: 23, seqType: [ '1', '2', '3' ], type: 'Array' },
{ seqNum: 6, seqType: '10', type: 'String' },
{ seqNum: 24, seqType: '10', type: 'String' },
{ seqNum: 8, seqType: [ [ 1 ], [ 2 ], [ 3 ] ], type: 'Array' },
{ seqNum: 22, seqType: [ [ 1 ], [ 2 ], [ 3 ] ], type: 'Array ' },
{
seqNum: 13,
seqType: ObjectId("6239e3922604d5a7478df071"),
type: 'ObjectId'
},
{ seqNum: 10, seqType: true, type: 'Boolean' },
{
seqNum: 12,
seqType: ISODate("2022-03-22T14:56:18.100Z"),
type: 'Date'
},
{
seqNum: 11,
seqType: Timestamp({ t: 1647960978, i: 1 }),
type: 'Timestamp'
}
  • 배열 비교에서

    • 보다 작음 비교 또는 오름차순 정렬은 배열의 가장 작은 요소를 BSON 유형 정렬 순서에 따라 비교합니다.

    • 보다 큼 비교 또는 내림차순 정렬은 배열에서 가장 큰 요소를 역 BSON 유형 정렬 순서에 따라 비교합니다.

    • 값이 하나의 요소 배열(예: [ 1 ])인 필드를 배열이 아닌 필드(예: 2)와 비교할 때 비교 대상은 12입니다.

    • 빈 배열(예: [ ])을 비교하면 빈 배열을 null 값보다 작거나 누락된 필드 값으로 간주합니다.

  • 숫자 형식(Int32, Long, Decimal128, Double)은 다른 유형과 비교해 동일합니다.

  • 숫자 BSON 유형에서 숫자 유형을 정렬합니다.

    • Int32

    • Long

    • Decimal128

    • 더블

← 쿼리를 지원하는 인덱스 생성