MongoDB 인덱스의 종류, 생성/관리 방법, 실행 계획 분석을 정리합니다.
인덱스 종류
| 종류 |
설명 |
용도 |
| Single Field |
단일 필드 인덱스 |
기본 검색 |
| Compound |
복합 인덱스 (다중 필드) |
다중 조건 검색 |
| Multikey |
배열 필드 인덱스 |
배열 요소 검색 |
| Text |
전문 검색 인덱스 |
텍스트 검색 |
| 2dsphere |
지리공간 인덱스 |
위치 기반 검색 |
| Hashed |
해시 인덱스 |
샤딩 키 |
| TTL |
자동 만료 인덱스 |
시간 기반 자동 삭제 |
| Unique |
유니크 인덱스 |
중복 방지 |
| Partial |
부분 인덱스 (조건부) |
특정 조건 문서만 |
| Wildcard |
와일드카드 인덱스 |
동적 필드 검색 |
인덱스 생성 및 관리
생성
// 단일 필드 (1: 오름차순, -1: 내림차순)
db.users.createIndex({ email: 1 })
// 유니크 인덱스
db.users.createIndex({ email: 1 }, { unique: true })
// 복합 인덱스
db.users.createIndex({ dept: 1, salary: -1 })
// 배열 필드 (Multikey, 자동 감지)
db.users.createIndex({ skills: 1 })
// TTL 인덱스 (60초 후 자동 삭제)
db.sessions.createIndex({ created_at: 1 }, { expireAfterSeconds: 60 })
// 부분 인덱스 (조건 만족하는 문서만)
db.users.createIndex(
{ email: 1 },
{ partialFilterExpression: { is_active: true } }
)
// 텍스트 인덱스
db.articles.createIndex({ title: "text", content: "text" })
// 지리공간 인덱스
db.stores.createIndex({ location: "2dsphere" })
// 와일드카드 인덱스 (동적 필드)
db.products.createIndex({ "attributes.$**": 1 })
// 백그라운드 생성 (MongoDB 4.2+ 기본)
db.users.createIndex({ name: 1 }, { name: "idx_name" })
조회
// 인덱스 목록
db.users.getIndexes()
// 인덱스 크기
db.users.stats().indexSizes
// 인덱스 사용 통계
db.users.aggregate([{ $indexStats: {} }])
삭제
// 이름으로 삭제
db.users.dropIndex("idx_name")
// 정의로 삭제
db.users.dropIndex({ email: 1 })
// _id 제외 모든 인덱스 삭제
db.users.dropIndexes()
실행 계획 (explain)
// 실행 계획 확인
db.users.find({ dept: "개발팀" }).explain("executionStats")
주요 확인 항목
| 항목 |
설명 |
좋은 값 |
| stage |
실행 단계 |
IXSCAN (인덱스 사용) |
| nReturned |
반환된 문서 수 |
적을수록 좋음 |
| totalDocsExamined |
검사한 문서 수 |
nReturned에 가까울수록 좋음 |
| totalKeysExamined |
검사한 인덱스 키 수 |
nReturned에 가까울수록 좋음 |
| executionTimeMillis |
실행 시간 (ms) |
작을수록 좋음 |
Stage 종류
| Stage |
설명 |
성능 |
| COLLSCAN |
컬렉션 전체 스캔 |
나쁨 |
| IXSCAN |
인덱스 스캔 |
좋음 |
| FETCH |
인덱스 후 문서 접근 |
보통 |
| SORT |
메모리 정렬 |
인덱스 정렬 권장 |
| PROJECTION_COVERED |
인덱스만으로 처리 (커버링) |
최고 |
복합 인덱스 설계 (ESR 규칙)
복합 인덱스의 필드 순서는 성능에 큰 영향을 미칩니다.
ESR 규칙 (Equality → Sort → Range)
| 순서 |
유형 |
예시 |
| 1 |
Equality (등치) |
dept = "개발팀" |
| 2 |
Sort (정렬) |
ORDER BY salary DESC |
| 3 |
Range (범위) |
age >= 25 |
// 쿼리: dept = "개발팀" AND age >= 25 ORDER BY salary DESC
// 최적 인덱스: { dept: 1, salary: -1, age: 1 }
db.users.createIndex({ dept: 1, salary: -1, age: 1 })
TTL 인덱스 (자동 만료)
지정된 시간이 지나면 문서를 자동으로 삭제합니다. 세션, 로그, 임시 데이터에 유용합니다.
// 30분 후 자동 삭제
db.sessions.createIndex({ created_at: 1 }, { expireAfterSeconds: 1800 })
// 특정 시점에 삭제 (expireAt 필드 값 기준)
db.events.createIndex({ expireAt: 1 }, { expireAfterSeconds: 0 })
db.events.insertOne({
event: "promotion",
expireAt: new Date("2026-12-31T23:59:59Z") // 이 시점에 삭제
})
텍스트 인덱스와 검색
// 텍스트 인덱스 생성
db.articles.createIndex({ title: "text", content: "text" })
// 텍스트 검색
db.articles.find({ $text: { $search: "MongoDB 튜토리얼" } })
// 관련도 점수와 함께
db.articles.find(
{ $text: { $search: "MongoDB" } },
{ score: { $meta: "textScore" } }
).sort({ score: { $meta: "textScore" } })
// 구문 검색 (정확한 문구)
db.articles.find({ $text: { $search: '"MongoDB 설치"' } })
// 제외 검색
db.articles.find({ $text: { $search: "MongoDB -설치" } })
인덱스 설계 가이드라인
| 원칙 |
설명 |
| 쿼리 패턴 분석 |
자주 사용하는 쿼리 기준으로 설계 |
| ESR 규칙 준수 |
Equality → Sort → Range 순서 |
| 커버링 인덱스 활용 |
쿼리에 필요한 필드를 모두 인덱스에 포함 |
| 과도한 인덱스 지양 |
쓰기 성능 저하, 메모리 사용 증가 |
| 부분 인덱스 활용 |
조건에 맞는 문서만 인덱싱 |
| 인덱스 사용 모니터링 |
$indexStats로 미사용 인덱스 확인 |
관련된 글 (mongodb > lecture-mongodb)