크리스티나 초도로 , 섀넌 브래드쇼 , 오언 브라질 님의 "몽고DB 완벽 가이드" 책을 정리한 포스팅 입니다.
1. 인덱싱 소개
컬렉션 스캔
- 인덱스를 사용하지 않는 쿼리
- 컬렉션 풀스캔이 일어남
기본 인덱스
- 자주 사용되는 쿼리와 빨리 수행되어야 하는 쿼리의 공통적인 키 셋에 적용
예제) 기본 인덱스 생성
더보기
db.users.createIndex({"name": 1}) # name 필드에 대해 인덱스 생성 (1: 오름차순, -1: 내림차순)
복합 인덱스
- 복수개의 키로 구성된 인덱스
- 모든 값을 정렬된 순서로 보관 (정렬 쿼리 성능 향상)
항목 | 설명 | 비고 / 핵심 포인트 |
선택성 (Selectivity) | 인덱스가 얼마나 좁은 범위를 스캔하는지 |
선택성이 높을수록 좋음 (레코드 수 적을수록)
|
컬럼 구성 우선순위 | 인덱스 필드의 구성 순서 전략 |
① 동등 필터 (선택성 높임)
② 정렬 필드 (정렬 비용 제거) ③ 범위 필터 (스캔 대상 결정) |
키 방향 선택 | 인덱스의 오름차순/내림차순 방향 설정 |
- 정렬에 영향을 줌
- 모든 키 방향은 동일하게 지정해야 효율적 - 역방향 인덱스끼리는 동등 |
커버드 쿼리 | 인덱스만 보고 결과를 생성하는 쿼리 |
- 문서 스캔 없음
- 반환 필드는 인덱스에만 있어야 함 - _id는 명시적으로 꺼야 함 (_id: 0) |
암시적 인덱스 | 복합 인덱스의 접두사(prefix) 부분만으로 쿼리 수행 |
- 왼쪽부터 순서대로만 사용 가능
- 중간 필드만 필터로 쓰면 인덱스 못 씀 |
예제) 복합 인덱스
더보기
# (age, name) 순서로 둘다 오름차순 정렬되어 저장된 인덱스
db.users.createIndex({ "age": 1, "name": 1 })
db.users.find({age: 37}).sort({name: -1}) # age=37 전체 범위를 인덱스로 읽고, 결과를 뒤집어 반환함.
db.users.find({age: {$gte: 37, $lte: 39}}) # age 37~39까지만 인덱스에서 읽음. 별도 정렬 작업 없음.
db.users.find({age: {$gte: 37, $lte: 39}}).sort({name: 1}) # (2)와 결과, 처리 방식 둘 다 동일.
예제) 키 방향 선택
더보기
db.users.createIndex({class_id: 1, final_grade: 1, student_id: 1})
db.students.find({student_id: {$gt: 500000}, class_id: 54}).sort({final_grade: 1})
예제) 커버드 쿼리
더보기
# covered query
db.students.find({name: "Curry"}, {name: 1, age: 1, _id: 0}) # _id값 명시적으로 꺼주기
예제) 암시적 인덱스
더보기
# implicit index
db.users.createIndex({ name: 1, age: 1, score: 1 })
db.users.find({ name: "Noah" }) ✅ 사용 가능
db.users.find({ name: "Noah", age: 25 }) ✅ 사용 가능
db.users.find({ name: "Noah", score: 90 }) ✅ 사용 가능 (score도 projection or sort일 경우)
db.users.find({ age: 25 }) ❌ 사용 불가
db.users.find({ score: 90 }) ❌ 사용 불가
인덱스 선택 기준
항목 | 설명 |
쿼리 모양 확인 |
조건 필드와 정렬 필드 분석
|
쿼리 플랜 생성 |
인덱스 조합으로 가능한 실행 계획을 모두 테스트 (직접 백그라운드에서 병렬로 실행해봄)
|
최종 선택 | 여러 레이스로 경쟁시켜, 가장 높은 성능에 도달하는 쿼리를 선택 |
비효율적인 연산자
기준 | $ne | $not | $nin |
의미 | 같지 않음 | 조건 부정 | 주어진 목록에 없음 |
인덱스 활용성 | 거의 없음 | 거의 없음 |
없음 (항상 COLLSCAN)
|
비효율 이유 | 제외할 항목 외 모든 인덱스 항목 확인 필요 | 범위나 정규표현식 부정을 역으로 처리해야 함 |
모든 문서를 스캔해야 함
|
예제
더보기
db.users.find({age: {$ne: 30}})
db.users.find({age: {$not: {$gt: 30}}})
db.users.find({age: {$nin: [20, 25, 30]}})
OR 쿼리
항목 | $or | $in |
조건 대상 | 여러 필드 가능 | 단일 필드만 가능 |
인덱스 사용 | 각 조건마다 인덱스 가능 |
단일 필드 인덱스만 사용
|
실행 횟수 | 조건 수만큼 쿼리 실행 | 한 번만 실행 |
일반 성능 | 보통 느림 (복잡도 ↑) | 보통 빠름 (단순 쿼리) |
예제
더보기
db.users.find({$or: [{age: 20}, {name: "noah"}]})
db.users.find({age: {$in: [20, 25, 30]}})
내장 도큐먼트 인덱싱
- 일반적인 키에 생성될 떄와 동일한 방식으로 내장 도큐먼트 키에 생성될 수 있음
- 내장 도큐먼트 전체를 인덱싱하면, 내장 도큐먼트 전체를 쿼리할 때만 도움이 됨
예제
더보기
db.students.createIndex({"loc.city": 1})
배열 인덱싱
- 배열 요소마다 인덱스 작업이 수행되므로 성능이 떨어질 수 있음
- 배열의 한 필드만 인덱싱할 수 있음 (전체 인덱싱 불가)
- 배열 인덱스는 다중키 인덱스로 인식됨
인덱스 카디널리티
- 카디널리티는 컬렉션의 한 필드에 대해 고윳값이 얼마나 많은지 나타냄
- 카디널리티가 높을수록 인덱싱이 더 도움이 됨 (검색 범위를 훨씬 작게 좁힐 수 있음)
- 복합 인덱스일 경우, 카디널리티가 높은 필드를 앞에 두면 도움이 됨
2. explain 출력
카테고리 | 항목 | 의미 | 설명 |
queryPlanner | winningPlan | 선정된 쿼리 플랜 정보 | |
rejectedPlans | 실패한 쿼리 플랜 정보들 | ||
executionStats | nReturned | 반환된 문서 수 | |
executionTimeMillis | 전체 실행 시간 | ||
totalKeysExamined | 인덱스가 읽은 도큐먼트 수 | 0개: 인덱스 사용 안 함 | |
totalDocsExamined | 실제로 읽은 도큐먼트 수 | ||
executionStages.stage | 실행 계획 단계 | - COLLSCAN: 전체 컬렉션 스캔 수행 |
|
executionStages.isMultiKey | 다중키 인덱스 사용 여부 | ||
executionStages.indexBounds |
인덱스 사용 범위 | ||
executionStages.docsExamined | 단계 내 읽은 문서 수 | 실행 계획을 통해 읽은 문서 수 |
예시) 실행계획 확인
더보기
db.users.find().explain("executionStats")
힌트
예시) 인덱스 사용 강제
더보기
db.users.find().hint({name: 1, age: 1})
3. 인덱스를 생성하지 않는 경우
- 인덱스는 컬렉션에서 가져와야 할 부분이 많을수록 비효율적임
- 인덱스를 한번 사용하는데 두번의 조회가 발생하기 때문 (인덱스 서칭 - 인덱스 포인터를 통한 도큐먼트 서칭)
- 가져와야 할 부분이 많으면 컬렉션 스캔이 성능이 덜 들수 있음
4. 인덱스 종류
고유 인덱스
- 각 값이 인덱스에 최대 한 번 나타나도록 보장하는 인덱스
- null을 값으로 취급함
- 중복값으로 추가하거나 수정할 경우, duplicate key error 발생
예제
더보기
db.students.createIndex({student_id: 1}, {unique: true})
db.students.createIndex({student_id: 1, name: 1}, {unique: true})
부분 인덱스
- 특정 조건을 만족하는 도큐먼트에만 적용되는 인덱스
- 인덱스 크기 및 성능을 높일 수 있는 전략적 기능
- 원하는 조건에 맞게 쿼리를 작성해야 효과를 볼 수 있음
예제
더보기
db.students.createIndex({email: 1}, {unique: true, partialFilterExpression: {status: "active"}})
db.students.find({status: "active", email: "user@example.com"})
5. 인덱스 관리
- 인덱스는 system.indexes 컬렉션에 저장됨
인덱스 정보
- getIndexes(): 메타 정보 확인 가능 (버전, 키 구성, 이름)
인덱스 식별
- 인덱스 이름은 '키명_방향' 의 조합으로 이루어짐
- ex) age_1_name_1
인덱스 삭제
- dropIndex(이름)
'Database > MongoDB' 카테고리의 다른 글
[몽고DB 완벽 가이드] 7. 집계 프레임워크 (1) | 2025.04.30 |
---|---|
[몽고DB 완벽 가이드] 6. 특수 인덱스와 컬렉션 유형 (0) | 2025.04.30 |
[몽고DB 완벽 가이드] 4. 쿼리 (1) | 2025.04.28 |
[몽고DB 완벽 가이드] 3. 도큐먼트 생성, 갱신, 삭제 (0) | 2025.04.28 |
[몽고DB 완벽 가이드] 2. 몽고DB 기본 (1) | 2025.04.27 |