1. MongoDB Partial 인덱스
쿼리를 효율적으로 사용하기 위해 MongoDB 는 인덱스를 제공한다.
MongoDB 에서 인덱스 유형은 매우 다양하다.
1-1. MongoDB 인덱스 유형
유형 | 설명 |
단일 필드 인덱스 | 특정 필드에 대한 인덱스 |
복합 인덱스 | 여러 필드를 조합한 인덱스 |
해시 인덱스 | 해시된 값으로 인덱스 |
텍스트 인덱스 | 텍스트 검색을 위한 인덱스 |
2dsphere 인덱스 | 지리공간 데이터에 대한 인덱스 |
유일 인덱스 | 유일한 값만 허용 - 로컬서버 기준에 한정, 샤드서버로 데이터가 분산되는 경우에는 어플리케이션에서 처리 |
이러한 인덱스의 옵션에서 Partial 을 제공하는데 , 이 때는 이 필드에 포함된 값이 특정 값 이상일 때만 인덱스에 포함시킨다.
2. MongoDB 테스트 버전
유형 | 버전 | 구성 |
mongosh | 2.2.10 | |
mongodb | 7.0.12 | Config : 1개, 포트 20000 Route : 1개, 포트 20001 Shard1 : 1개, 포트 30001 Shard2 : 1개, 포트 40001 |
3. Partial 인덱스 생성
3-1. Partial 인덱스 생성
partialFilterExpression 에서 특정 필드가 특정 값 이상일 때 인덱스 구성에 포함되도록 설정한다.
Score 인덱스를 만드는데 이 때 50 이상인 경우에 대해서만 인덱스를 생성한다.
따라서 50 이상인 값을 조회할 때만 인덱스를 사용할 수 있다.
[direct: mongos] indexDB> db.partialIndex.createIndex ( { Score : 1 } , { partialFilterExpression : { Score : { $gt : 50 } } } )
Score_1
[direct: mongos] indexDB> db.partialIndex.insert ( { "Name" : "N1" , "Score" : 39 } )
DeprecationWarning: Collection.insert() is deprecated. Use insertOne, insertMany, or bulkWrite.
{
acknowledged: true,
insertedIds: { '0': ObjectId('66b9d3ade51e7f66d81b157d') }
}
[direct: mongos] indexDB> db.partialIndex.insert ( { "Name" : "N2" , "Score" : 51 } )
{
acknowledged: true,
insertedIds: { '0': ObjectId('66b9d3b3e51e7f66d81b157e') }
}
[direct: mongos] indexDB> db.partialIndex.insert ( { "Name" : "N3" , "Score" : 72 } )
{
acknowledged: true,
insertedIds: { '0': ObjectId('66b9d3b8e51e7f66d81b157f') }
}
[direct: mongos] indexDB> db.partialIndex.insert ( { "Name" : "N4" , "Score" : 49 } )
{
acknowledged: true,
insertedIds: { '0': ObjectId('66b9d3bee51e7f66d81b1580') }
}
[direct: mongos] indexDB> db.partialIndex.insert ( { "Name" : "N5" } )
{
acknowledged: true,
insertedIds: { '0': ObjectId('66b9d3ebe51e7f66d81b1581') }
}
4. Partial 인덱스 조회
위에서 만들어 놓은 Partial 인덱스를 사용하는지 실행계획으로 확인해본다.
4-1. Partial 범위에 포함된 데이터 조회
50 이상인 데이터를 조회하면 winningPlan.stage 에 FETCH 로 나온다.
[direct: mongos] indexDB> db.partialIndex.find({"Score": { $gt : 50 } } ).explain()
{
queryPlanner: {
mongosPlannerVersion: 1,
winningPlan: {
stage: 'SINGLE_SHARD',
shards: [
{
shardName: 'rs1',
connectionString: 'rs1/127.0.0.1:30001',
serverInfo: {
host: 'ip-172-31-7-169.ap-northeast-2.compute.internal',
port: 30001,
version: '7.0.12',
gitVersion: 'b6513ce0781db6818e24619e8a461eae90bc94fc'
},
namespace: 'indexDB.partialIndex',
indexFilterSet: false,
parsedQuery: { Score: { '$gt': 50 } },
queryHash: '14DDA81F',
planCacheKey: '8FF5F16D',
maxIndexedOrSolutionsReached: false,
maxIndexedAndSolutionsReached: false,
maxScansToExplodeReached: false,
winningPlan: {
stage: 'FETCH',
inputStage: {
stage: 'IXSCAN',
keyPattern: { Score: 1 },
indexName: 'Score_1',
isMultiKey: false,
multiKeyPaths: { Score: [] },
isUnique: false,
isSparse: false,
isPartial: true,
indexVersion: 2,
direction: 'forward',
indexBounds: { Score: [ '(50, inf.0]' ] }
}
},
rejectedPlans: []
}
]
}
},
serverInfo: {
host: 'ip-172-31-7-169.ap-northeast-2.compute.internal',
port: 20001,
version: '7.0.12',
gitVersion: 'b6513ce0781db6818e24619e8a461eae90bc94fc'
},
serverParameters: {
internalQueryFacetBufferSizeBytes: 104857600,
internalQueryFacetMaxOutputDocSizeBytes: 104857600,
internalLookupStageIntermediateDocumentMaxSizeBytes: 104857600,
internalDocumentSourceGroupMaxMemoryBytes: 104857600,
internalQueryMaxBlockingSortMemoryUsageBytes: 104857600,
internalQueryProhibitBlockingMergeOnMongoS: 0,
internalQueryMaxAddToSetBytes: 104857600,
internalDocumentSourceSetWindowFieldsMaxMemoryBytes: 104857600,
internalQueryFrameworkControl: 'trySbeRestricted'
},
command: {
find: 'partialIndex',
filter: { Score: { '$gt': 50 } },
lsid: { id: UUID('84e46ccb-a44d-48ca-817b-2a41d457dbf5') },
'$clusterTime': {
clusterTime: Timestamp({ t: 1723514261, i: 2 }),
signature: {
hash: Binary.createFromBase64('AAAAAAAAAAAAAAAAAAAAAAAAAAA=', 0),
keyId: 0
}
},
'$db': 'indexDB'
},
ok: 1,
'$clusterTime': {
clusterTime: Timestamp({ t: 1723514406, i: 2 }),
signature: {
hash: Binary.createFromBase64('AAAAAAAAAAAAAAAAAAAAAAAAAAA=', 0),
keyId: Long('0')
}
},
operationTime: Timestamp({ t: 1723514401, i: 1 })
}
4-2. Partial 범위에 포함되지 않는 데이터 조회
49 이상인 데이터를 조회하면 winningPlan.stage 에 COLLSCAN (테이블스캔) 으로 나온다.
[direct: mongos] indexDB> db.partialIndex.find({"Score": { $gt : 49 } } ).explain()
{
queryPlanner: {
mongosPlannerVersion: 1,
winningPlan: {
stage: 'SINGLE_SHARD',
shards: [
{
shardName: 'rs1',
connectionString: 'rs1/127.0.0.1:30001',
serverInfo: {
host: 'ip-172-31-7-169.ap-northeast-2.compute.internal',
port: 30001,
version: '7.0.12',
gitVersion: 'b6513ce0781db6818e24619e8a461eae90bc94fc'
},
namespace: 'indexDB.partialIndex',
indexFilterSet: false,
parsedQuery: { Score: { '$gt': 49 } },
queryHash: '14DDA81F',
planCacheKey: '81864C8B',
maxIndexedOrSolutionsReached: false,
maxIndexedAndSolutionsReached: false,
maxScansToExplodeReached: false,
winningPlan: {
stage: 'COLLSCAN',
filter: { Score: { '$gt': 49 } },
direction: 'forward'
},
rejectedPlans: []
}
]
}
},
serverInfo: {
host: 'ip-172-31-7-169.ap-northeast-2.compute.internal',
port: 20001,
version: '7.0.12',
gitVersion: 'b6513ce0781db6818e24619e8a461eae90bc94fc'
},
serverParameters: {
internalQueryFacetBufferSizeBytes: 104857600,
internalQueryFacetMaxOutputDocSizeBytes: 104857600,
internalLookupStageIntermediateDocumentMaxSizeBytes: 104857600,
internalDocumentSourceGroupMaxMemoryBytes: 104857600,
internalQueryMaxBlockingSortMemoryUsageBytes: 104857600,
internalQueryProhibitBlockingMergeOnMongoS: 0,
internalQueryMaxAddToSetBytes: 104857600,
internalDocumentSourceSetWindowFieldsMaxMemoryBytes: 104857600,
internalQueryFrameworkControl: 'trySbeRestricted'
},
command: {
find: 'partialIndex',
filter: { Score: { '$gt': 49 } },
lsid: { id: UUID('84e46ccb-a44d-48ca-817b-2a41d457dbf5') },
'$clusterTime': {
clusterTime: Timestamp({ t: 1723514406, i: 2 }),
signature: {
hash: Binary.createFromBase64('AAAAAAAAAAAAAAAAAAAAAAAAAAA=', 0),
keyId: 0
}
},
'$db': 'indexDB'
},
ok: 1,
'$clusterTime': {
clusterTime: Timestamp({ t: 1723514456, i: 2 }),
signature: {
hash: Binary.createFromBase64('AAAAAAAAAAAAAAAAAAAAAAAAAAA=', 0),
keyId: Long('0')
}
},
operationTime: Timestamp({ t: 1723514451, i: 1 })
}
5. Partial 인덱스 주의사항
Partial 인덱스는 NULL 비교가 상황에 따라 때로는 다르게 떄로는 같게 취급된다.
따라서 조회되어야 하는 데이터가 조회되지 않을수도 있다.
위에서 Name 이 N5 인 도큐먼트를 적재할 때 Score 를 명시하지 않았다.
그리고 N6 에서 Score 는 null 이라고 추가로 명시해 적재했다.
이 후 Partial 인덱스 힌트를 사용하지 않을때와 사용할때 조회해본 결과를 비교해본다.
[direct: mongos] indexDB> db.partialIndex.insert( { "Name" : "N6", "Score":null } )
{
acknowledged: true,
insertedIds: { '0': ObjectId('66babf0001027809e11b157d') }
}
# 일반적인 조회로는 N5 와 N6 이 출력된다.
[direct: mongos] indexDB> db.partialIndex.find({"Score": null } )
[
{ _id: ObjectId('66b9d3ebe51e7f66d81b1581'), Name: 'N5' },
{
_id: ObjectId('66babf0001027809e11b157d'),
Name: 'N6',
Score: null
}
]
# Partial 인덱스 힌트를 사용하면 조회되지 않는것을 확인할 수 있다.
# 결과 빈 값
[direct: mongos] indexDB> db.partialIndex.find({"Score": null } ).hint( { "Score" : 1 } )
힌트 없을 때 | |
힌트 있을 때 |
'Database > MongoDB 실습' 카테고리의 다른 글
[MongoDB] 글로벌 잠금 설정과 해제방법 (0) | 2024.08.21 |
---|---|
[MongoDB] TTL 인덱스 생성과 도큐먼트 적재 (0) | 2024.08.13 |
[MongoDB] 유니크 인덱스 생성 방법 (0) | 2024.08.12 |
[MongoDB] 전문검색 인덱스 사용법과 주의사항 (0) | 2024.08.06 |
[MongoDB] 멀티 키 인덱스 범위조건 사용하는 방법 (0) | 2024.08.06 |