Database/MongoDB
[MongoDB] MongoDB 인덱스 1편 - 인덱스 원리와 주요 유형들
comgu
2025. 6. 1. 11:41
반응형
MongoDB를 사용하다 보면 데이터가 많아질수록 쿼리 성능이 현저히 떨어지는 경험을 하게 된다. 이때 가장 효과적인 해결책이 바로 인덱스(Index)이다. 인덱스는 마치 책의 색인처럼 특정 데이터를 빠르게 찾을 수 있도록 도와주는 핵심 기능이다.
목차
인덱스의 기본 원리
MongoDB는 B-tree 자료구조를 사용해 인덱스를 구성한다. B-tree는 정렬된 상태로 데이터를 저장하여 빠른 검색, 삽입, 삭제를 가능하게 한다.
인덱스의 내부 구조 (B-Tree)
[M]
/ \
[D,G] [P,S]
/ | \ / | \
[A] [E] [H] [N] [Q] [T]
- 정렬된 상태로 저장
- 이진 탐색으로 빠른 검색 (O(log n))
- 범위 검색도 효율적
인덱스가 없을 때 vs 있을 때
인덱스가 없는 경우 (Collection Scan):
- 컬렉션의 모든 문서를 하나씩 확인
- 100만 개 문서가 있다면 최악의 경우 100만 번 확인
인덱스가 있는 경우 (Index Scan):
- B-tree를 통해 필요한 데이터만 빠르게 찾음
- 로그 시간 복잡도로 훨씬 빠른 검색
# PyMongo 기본 인덱스 생성 예제
collection.create_index("username")
collection.create_index([("age", 1)]) # 1: 오름차순, -1: 내림차순
주요 인덱스 유형
1. Single Field Index (단일 필드 인덱스)
가장 기본적인 형태로, 하나의 필드에 대해서만 인덱스를 생성한다.
# 사용자명에 인덱스 생성
collection.create_index("username")
# 빠른 검색 가능
result = await collection.find({"username": "john_doe"})
특징:
- 구현이 간단하고 직관적
- 해당 필드로만 검색할 때 최적의 성능
- 메모리 사용량이 적음
2. Compound Index (복합 인덱스)
여러 필드를 조합한 인덱스이다. 필드의 순서가 매우 중요하다.
# 사용자명과 나이를 함께 인덱싱
collection.create_index([("username", 1), ("age", -1)])
중요한 특징 - 인덱스 프리픽스 규칙:
- username만으로도 인덱스 활용 가능 ✅
- age만으로는 이 인덱스 사용 불가 ❌
- username + age 조합으로 최적의 성능 ✅
3. Multikey Index (다중키 인덱스)
배열 필드에 자동으로 생성되는 인덱스이다.
# tags 필드가 ["python", "mongodb", "fastapi"] 배열이라면
collection.create_index("tags")
# 각 태그 값으로 검색 가능
result = await collection.find({"tags": "python"})
동작 원리:
- 배열의 각 요소에 대해 개별 인덱스 엔트리 생성
- 배열 내 어떤 값으로도 빠른 검색 가능
4. Text Index (텍스트 인덱스)
텍스트 검색을 위한 특별한 인덱스이다.
collection.create_index([("title", "text"), ("content", "text")])
# 단어 기반 텍스트 검색
result = await collection.find({"$text": {"$search": "python mongodb"}})
특징:
- 단어별로 분해하여 저장
- 대소문자 무관 검색
- 언어별 불용어 처리
- 컬렉션당 하나만 생성 가능
5. Geospatial Index (지리공간 인덱스)
지리적 위치 데이터를 위한 인덱스이다.
# 2dsphere 인덱스 생성
collection.create_index([("location", "2dsphere")])
# 근처 위치 검색
result = await collection.find({
"location": {
"$near": {
"$geometry": {"type": "Point", "coordinates": [127.0276, 37.4979]},
"$maxDistance": 1000
}
}
})
활용 사례:
- 배달 앱의 근처 음식점 찾기
- 위치 기반 서비스
- 지도 애플리케이션
6. Sparse Index (희소 인덱스)
인덱싱된 필드가 존재하는 문서만 포함하는 인덱스이다.
collection.create_index("optional_field", sparse=True)
장점:
- 선택적 필드에 유용
- 인덱스 크기 절약
- null 값이 많은 필드에 효과적
7. Partial Index (부분 인덱스)
특정 조건을 만족하는 문서만 인덱싱한다.
collection.create_index(
"username",
partialFilterExpression={"age": {"$gte": 18}}
)
활용 사례:
- 성인 사용자만 인덱싱
- 활성 사용자만 인덱싱
- 특정 조건의 데이터만 빠른 검색
8. TTL Index (Time To Live)
문서의 자동 만료를 위한 인덱스다.
# 1시간 후 자동 삭제
collection.create_index("createdAt", expireAfterSeconds=3600)
활용 사례:
- 세션 데이터 관리
- 임시 파일 자동 정리
- 로그 데이터 보관 기간 관리
인덱스 성능 모니터링
쿼리 실행 계획 확인
# 쿼리가 인덱스를 사용하는지 확인
explain_result = await collection.find({"username": "john"}).explain()
stage = explain_result["queryPlanner"]["winningPlan"]["stage"]
if stage == "IXSCAN":
print("인덱스 사용됨 ✅")
elif stage == "COLLSCAN":
print("전체 컬렉션 스캔 발생 ⚠️")
인덱스 사용량 통계
# 인덱스 사용 통계 확인
index_stats = await db.command("collStats", "users", indexDetails=True)
print(index_stats["indexSizes"])
인덱스 설계 시 고려사항
1. 쿼리 패턴 분석
- 자주 사용되는 검색 조건 파악
- 정렬 기준 확인
- 필터링 조건 우선순위 결정
2. 성능 vs 비용 트레이드오프
- 장점: 쿼리 성능 대폭 향상
- 단점: 스토리지 공간 사용, 쓰기 성능 약간 저하
3. 인덱스 최적화 원칙
- 필요한 인덱스만 생성
- 중복 인덱스 제거
- 정기적인 성능 모니터링
다음 편 예고
2편에서는 복합 인덱스의 심화 원리와 효과적인 설계 방법에 대해 자세히 알아본다. 특히 인덱스 프리픽스 규칙과 필드 순서가 성능에 미치는 영향을 실제 예제와 함께 살펴본다.
반응형