// users 컬렉션의 email 필드에 오름차순(1) 인덱스 생성
db.users.createIndex({ email: 1 })
// users 컬렉션의 status 필드(오름차순)와 age 필드(내림차순)로 복합 인덱스 생성
db.users.createIndex({ status: 1, age: -1 })
{ status: 1, age: -1 }
로 인덱스를 생성하면, 다음과 같은 쿼리들에 효율적으로 사용될 수 있습니다.status
필드만으로 검색하는 쿼리status
필드와 age
필드를 함께 검색하는 쿼리age
필드만으로 검색하는 쿼리에는 이 복합 인덱스가 효율적으로 사용되기 어렵습니다. (첫 번째 인덱스 키인 status
가 쿼리에 포함되지 않았기 때문입니다.)
따라서 createIndex()
db.collection.createIndex({ fieldName: 1 }) // 단일 필드 오름차순
db.collection.createIndex({ field1: 1, field2: -1 }) // 복합 필드
getIndexes()
- 컬렉션에 생성된 모든 인덱스 목록을 보여줍니다.db.collection.getIndexes()
dropIndex()
- 특정 인덱스를 삭제합니다. 인덱스 이름 또는 키 패턴을 인자로 전달합니다.db.collection.dropIndex("indexName") // 이름으로 삭제
db.collection.dropIndex({ field1: 1, field2: -1 }) // 키 패턴으로 삭제
explain()
명령어는 find
, aggregate
, update
, delete
등 다양한 명령어 뒤에 붙여서 해당 작업이 어떻게 실행될 것인지(또는 실제로 어떻게 실행되었는지)에 대한 // find 쿼리의 실행 계획 보기
db.users.find({ status: "active", age: { $gt: 30 } }).explain()
// aggregate 파이프라인의 실행 계획 보기
db.users.aggregate([...pipeline...]).explain()
explain()
명령어는 여러 수준의 상세 정보(verbosity mode)를 제공합니다. 일반적으로 'executionStats' 모드를 사용하여 실제 실행 통계까지 확인하는 것이 유용합니다.db.users.find({ ... }).explain("executionStats")
myPracticeDB> db.users.find({ status: "active", age: { $gt: 30 } }).explain("executionStats")
{
explainVersion: '1',
queryPlanner: {
namespace: 'myPracticeDB.users',
parsedQuery: {
'$and': [ { status: { '$eq': 'active' } }, { age: { '$gt': 30 } } ]
},
indexFilterSet: false,
queryHash: '3C18BE05',
planCacheShapeHash: '3C18BE05',
planCacheKey: '16EDF2A1',
optimizationTimeMillis: 0,
maxIndexedOrSolutionsReached: false,
maxIndexedAndSolutionsReached: false,
maxScansToExplodeReached: false,
prunedSimilarIndexes: false,
winningPlan: {
isCached: false,
stage: 'COLLSCAN',
filter: {
'$and': [ { status: { '$eq': 'active' } }, { age: { '$gt': 30 } } ]
},
direction: 'forward'
},
rejectedPlans: []
},
executionStats: {
executionSuccess: true,
nReturned: 0,
executionTimeMillis: 0,
totalKeysExamined: 0,
totalDocsExamined: 3,
executionStages: {
isCached: false,
stage: 'COLLSCAN',
filter: {
'$and': [ { status: { '$eq': 'active' } }, { age: { '$gt': 30 } } ]
},
nReturned: 0,
executionTimeMillisEstimate: 0,
works: 4,
advanced: 0,
needTime: 3,
needYield: 0,
saveState: 0,
restoreState: 0,
isEOF: 1,
direction: 'forward',
docsExamined: 3
}
},
queryShapeHash: '87856776218A3BB688005500CB2DB5CAC0C1CFDF3DEAF739549229E4718056FC',
command: {
find: 'users',
filter: { status: 'active', age: { '$gt': 30 } },
'$db': 'myPracticeDB'
},
serverInfo: {
host: 'LDH',
port: 27017,
version: '8.0.8',
gitVersion: '7f52660c14217ed2c8d3240f823a2291a4fe6abd'
},
serverParameters: {
internalQueryFacetBufferSizeBytes: 104857600,
internalQueryFacetMaxOutputDocSizeBytes: 104857600,
internalLookupStageIntermediateDocumentMaxSizeBytes: 104857600,
internalDocumentSourceGroupMaxMemoryBytes: 104857600,
internalQueryMaxBlockingSortMemoryUsageBytes: 104857600,
internalQueryProhibitBlockingMergeOnMongoS: 0,
internalQueryMaxAddToSetBytes: 104857600,
internalDocumentSourceSetWindowFieldsMaxMemoryBytes: 104857600,
internalQueryFrameworkControl: 'trySbeRestricted',
internalQueryPlannerIgnoreIndexWithCollationForRegex: 1
},
ok: 1
}
explain()
결과는 JSON 형태로 다양한 정보를 담고 있지만, 성능 분석 시 특히 주목해야 할 부분은 다음과 같습니다.queryPlanner.winningPlan.stage
: MongoDB 쿼리 옵티마이저가 선택한 최적의 실행 계획 단계를 보여줍니다. 여기서 executionStats
: (executionStats 모드 사용 시) 실제 쿼리 실행에 대한 통계를 보여줍니다.
executionStats.nReturned
: 쿼리 결과로 반환된 문서 수.executionStats.totalKeysExamined
: 쿼리 실행 중 확인한 인덱스 키의 수. (IXSCAN 시)executionStats.totalDocsExamined
: 쿼리 실행 중 확인한 문서의 수. (COLLSCAN 시 컬렉션 전체 문서 수와 비슷할 수 있음)executionStats.executionTimeMillis
: 쿼리 실행에 걸린 시간 (밀리초).totalDocsExamined
수가 IXSCAN에서는 1인 반면, COLLSCAN에서는 3으로 나타납니다. 이는 인덱스를 사용하지 않고 컬렉션 전체를 스캔했음을 의미합니다.
핵심은 totalDocsExamined
수가 nReturned
수에 비해 얼마나 많은지를 보는 것입니다. 만약 인덱스를 잘 사용했다면 (IXSCAN), 이 두 값의 차이가 크지 않을 것입니다. 반면 컬렉션 스캔(COLLSCAN)이 발생하면 totalDocsExamined
수가 매우 커지고 실행 시간도 길어질 가능성이 높습니다. COLLSCAN이 발견되면 해당 쿼리에 적합한 인덱스를 생성하거나 쿼리 자체를 수정하는 것을 고려해야 합니다.$match
, find
조건), 정렬 조건($sort
)에 사용되는 필드에 적절한 인덱스(단일 또는 복합)를 생성합니다. 복합 인덱스의 경우 필드 순서에 유의합니다.find
나 $project
스테이지를 사용하여 쿼리 결과로 꼭 필요한 필드만 가져옵니다. 불필요한 데이터를 네트워크로 전송하거나 메모리에 로드하는 것을 방지합니다..**
)가 오는 경우는 인덱스를 효율적으로 사용하기 어렵습니다. 가능하면 접두사(prefix) 기반 검색(^...
)을 사용하거나 텍스트 인덱스를 고려합니다.explain()
명령어를 사용하여 실행 계획을 확인하는 습관을 들입니다.db.serverStatus()
: MongoDB 서버의 현재 상태(연결 수, 잠금, 네트워크, 메모리 사용량 등)에 대한 포괄적인 정보를 제공합니다.db.stats()
: 현재 데이터베이스의 통계(문서 수, 데이터 크기, 인덱스 크기 등)를 보여줍니다.db.collection.stats()
: 특정 컬렉션의 통계를 보여줍니다.explain
명령어를 사용하여 다음 시간에는 MongoDB의 고급 기능들 중 하나인복제 세트(Replica Set)에 대해 더 깊이 알아보고, 실제로 어떻게 구성하고 장애 상황에 어떻게 대처하는지 살펴보겠습니다. 데이터의 안정성과 가용성을 높이는 중요한 내용입니다.
explain()
결과 분석 (공식 문서): https://www.mongodb.com/docs/manual/reference/explain-results/↗