2025년 10월 30일 작성
MongoDB Index Build Process - 색인 생성 방식과 전략
index build process는 index 생성 시 database의 가용성과 성능에 영향을 미치는 중요한 과정입니다.
Index Build Process : Index 생성 방식
- index build process는 MongoDB가 새로운 index를 생성하는 절차입니다.
- collection의 모든 document를 읽고 정렬하여 index 구조를 만듭니다.
- index 생성 방식에 따라 database의 가용성과 성능이 크게 달라집니다.
- 대용량 collection에서는 index 생성에 많은 시간이 소요될 수 있습니다.
- MongoDB는 version에 따라서 다양한 index build 방식을 지원합니다.
Index Build 방식의 종류
- MongoDB는 역사적으로 foreground와 background 두 가지 방식을 지원했습니다.
Foreground Index Build (Legacy)
- foreground build는 index 생성 중 database를 잠그는 방식입니다.
- MongoDB 4.2 이전 version의 기본 방식입니다.
- index 생성이 완료될 때까지 해당 database의 모든 read/write 작업이 차단됩니다.
- 매우 빠르지만 service 중단이 발생합니다.
// MongoDB 4.2 이전에서 foreground build (기본값)
db.collection.createIndex({ field: 1 });
- production 환경에서는 사용하지 않는 것이 좋습니다.
- 점검 시간(maintenance window)에만 사용해야 합니다.
Background Index Build (Legacy)
- background build는 index 생성 중에도 read/write 작업을 허용하는 방식입니다.
- MongoDB 4.2 이전 version에서
background: trueoption으로 지정합니다.
- MongoDB 4.2 이전 version에서
// MongoDB 4.2 이전에서 background build
db.collection.createIndex({ field: 1 }, { background: true });
-
foreground build보다 느리지만 service 중단 없이 index를 생성할 수 있습니다.
-
background build의 한계가 있습니다.
- 여러 background index를 동시에 생성할 수 없습니다.
- replica set의 모든 member에서 순차적으로 실행되어 시간이 오래 걸립니다.
- index 생성 중 server가 재시작되면 처음부터 다시 시작해야 합니다.
Hybrid Index Build (MongoDB 4.2+)
- hybrid build는 MongoDB 4.2부터 도입된 새로운 기본 방식입니다.
- foreground의 빠른 속도와 background의 가용성을 결합했습니다.
// MongoDB 4.2 이상에서 기본값
db.collection.createIndex({ field: 1 });
- hybrid build는 세 단계로 진행됩니다.
- 초기화 단계 : 짧은 시간 동안 write를 차단하고 index 생성을 준비합니다.
- build 단계 : read/write를 허용하면서 index를 생성합니다. 이 기간 동안의 write는 별도로 기록됩니다.
- 완료 단계 : 짧은 시간 동안 write를 차단하고 기록된 변경 사항을 index에 반영합니다.
-
대부분의 시간 동안 read/write가 가능하므로 service 중단이 최소화됩니다.
- MongoDB 4.2 이상에서는
background: trueoption이 무시됩니다.- 모든 index build는 자동으로 hybrid 방식으로 실행됩니다.
Index Build 시간과 Resource 사용
- index build는 상당한 resource를 소비하며, collection 크기에 따라 오래 걸릴 수 있습니다.
Build 시간 예상
- index build 시간은 다양한 요인에 따라 달라집니다.
- collection의 document 수와 크기.
- index field의 data type과 복잡도.
- server의 CPU와 disk 성능.
- 동시에 실행 중인 다른 작업.
- 일반적으로 수백만 개의 document를 가진 collection에서 index 생성은 수분에서 수시간이 걸릴 수 있습니다.
Resource 사용
- index build는 다음 resource를 집중적으로 사용합니다.
| Resource | 영향 |
|---|---|
| CPU | document를 읽고 정렬하는 데에 사용됩니다. |
| Memory | 정렬 작업과 임시 data 저장에 사용됩니다. |
| Disk I/O | collection을 읽고 index를 쓰는 데에 사용됩니다. |
| Disk 공간 | index file과 임시 file을 저장하는 데에 필요합니다. |
- index build 중에는 다른 query의 성능이 일시적으로 저하될 수 있습니다.
Build 진행 상황 확인
// 현재 진행 중인 index build 확인
db.currentOp({
$or: [
{ op: "command", "command.createIndexes": { $exists: true } },
{ op: "none", "msg": /^Index Build/ }
]
});
currentOp()을 통해 index build의 진행 상황을 monitoring할 수 있습니다.
// index build 통계 확인
db.collection.stats().indexBuilds;
Replica Set에서의 Index Build
- replica set 환경에서는 index build가 더 복잡하게 진행됩니다.
Rolling Index Build
- rolling index build는 replica set의 각 member에서 순차적으로 index를 생성하는 방법입니다.
- 전체 replica set의 가용성을 유지하면서 index를 생성할 수 있습니다.
sequenceDiagram
participant Primary
participant Secondary1
participant Secondary2
Note over Secondary1: 1. Secondary에서 먼저 생성
Secondary1->>Secondary1: Index Build
Note over Secondary2: 2. 다른 Secondary에서 생성
Secondary2->>Secondary2: Index Build
Note over Primary,Secondary1: 3. Primary 전환
Secondary1->>Primary: Step Down
Note over Primary: 4. 구 Primary에서 생성
Primary->>Primary: Index Build
- rolling build 절차는 다음과 같습니다.
- 하나의 secondary에서 index를 생성합니다.
- 완료되면 다른 secondary에서 index를 생성합니다.
- 모든 secondary에서 완료되면 primary를 step down합니다.
- 새로운 primary가 선출되고, 구 primary(현재 secondary)에서 index를 생성합니다.
- rolling build는 service 중단 없이 index를 추가할 수 있지만, 매우 오래 걸립니다.
Simultaneous Index Build (MongoDB 4.4+)
- simultaneous build는 MongoDB 4.4부터 도입된 방식으로, replica set의 모든 member에서 동시에 index를 생성합니다.
- rolling build보다 훨씬 빠르게 완료됩니다.
- primary에서
createIndex()명령을 실행하면 자동으로 모든 secondary에 전파됩니다.
// MongoDB 4.4 이상에서는 자동으로 simultaneous build
db.collection.createIndex({ field: 1 });
- primary와 secondary는 거의 동시에 index build를 시작합니다.
- 각 member는 독립적으로 hybrid build process를 실행합니다.
- 모든 member가 index build를 완료해야 전체 작업이 완료됩니다.
- 하나라도 실패하면 전체 replica set에서 index가 rollback됩니다.
Index Build 실패와 중단
- index build는 다양한 이유로 실패하거나 중단될 수 있습니다.
Build 실패 원인
- index build가 실패하는 주요 원인은 다음과 같습니다.
| 원인 | 설명 |
|---|---|
| Disk 공간 부족 | index file과 임시 file을 저장할 공간이 부족합니다. |
| Memory 부족 | 정렬 작업에 필요한 memory가 부족합니다. |
| Unique 제약 위반 | unique index 생성 시 중복 값이 발견됩니다. |
| Server 재시작 | old version에서는 build가 중단되고 다시 시작됩니다. |
| Timeout | maxTimeMS를 초과하면 build가 중단됩니다. |
- index build 실패 시 error message를 확인하여 원인을 파악해야 합니다.
Build 중단
// 진행 중인 index build 중단
db.killOp(operationId);
-
currentOp()으로 operation ID를 확인한 후killOp()으로 중단할 수 있습니다. -
MongoDB 4.4 이상에서는 index build를 중단하면 이미 생성된 부분이 보존되지 않습니다.
- 처음부터 다시 시작해야 합니다.
Build 재시작
- MongoDB 4.4 이상에서는 index build가 중단되면 server 재시작 시 자동으로 재개됩니다.
- 중간 상태가 저장되어 처음부터 다시 시작하지 않아도 됩니다.
// 재시작 가능한 index build 정보 확인
db.getSiblingDB("admin").system.indexBuilds.find();
Index Build 최적화 전략
- index build를 효율적으로 수행하기 위한 전략이 있습니다.
적절한 시간 선택
- 사용량이 적은 시간대(off-peak hours)에 index를 생성합니다.
- 야간이나 주말 등 traffic이 낮은 시간을 활용합니다.
- 중요한 business event 전에는 index 생성을 피합니다.
- Black Friday, Promotion 기간 등.
Resource 준비
- disk 공간을 충분히 확보합니다.
- 일반적으로 collection 크기의 최소 1.2배 이상의 여유 공간이 필요합니다.
- memory를 충분히 할당합니다.
maxIndexBuildMemoryUsageMegabytes설정을 조정하여 build 성능을 향상시킬 수 있습니다.
병렬 Build 제한
// 동시에 생성할 수 있는 index 수 제한
db.adminCommand({
setParameter: 1,
maxNumActiveUserIndexBuilds: 2
});
- 여러 index를 동시에 생성하면 resource 경쟁이 발생합니다.
- 순차적으로 생성하거나 동시 생성 수를 제한합니다.
단계적 배포
- 대규모 replica set에서는 단계적으로 index를 배포합니다.
- staging 환경에서 먼저 테스트합니다.
- production의 일부 secondary에서 시험 생성합니다.
- 문제가 없으면 전체 replica set에 적용합니다.
Index Rebuild
- 기존 index를 재생성해야 하는 경우가 있습니다.
Rebuild가 필요한 경우
- index가 손상되었거나 비효율적인 경우 rebuild가 필요합니다.
| 상황 | 설명 |
|---|---|
| Index 단편화 | 오랜 기간 사용으로 index가 단편화되어 성능이 저하됩니다. |
| 손상된 index | disk 오류나 비정상 종료로 index가 손상되었습니다. |
| Version upgrade | MongoDB version upgrade 시 index format이 변경될 수 있습니다. |
| 압축 | index를 재생성하여 disk 공간을 회수합니다. |
Rebuild 방법
// 특정 index rebuild
db.collection.reIndex({ index: "index_name" });
// collection의 모든 index rebuild
db.collection.reIndex();
reIndex()는 모든 index를 삭제하고 다시 생성합니다.- rebuild 중에는 해당 index를 사용할 수 없습니다.
// 더 안전한 방법 : 새 index 생성 후 기존 index 삭제
db.collection.createIndex({ field: 1 }); // 새 index 생성
db.collection.dropIndex("old_index_name"); // 기존 index 삭제
- production 환경에서는 새 index를 먼저 생성하고 기존 index를 삭제하는 것이 안전합니다.
대용량 Collection의 Index Build
- 수백만 개 이상의 document를 가진 대용량 collection에서는 특별한 전략이 필요합니다.
Bulk Insert 전 Index 생성
- 대량의 data를 insert할 계획이라면, insert 전에 index를 생성하지 않습니다.
// 1. index 없이 bulk insert 수행 (빠름)
db.collection.insertMany(largeDataArray);
// 2. insert 완료 후 index 생성
db.collection.createIndex({ field: 1 });
- index가 있으면 insert 시마다 index를 update해야 하므로 매우 느려집니다.
- 모든 data를 insert한 후 한 번에 index를 생성하는 것이 훨씬 빠릅니다.
Sharding 고려
- 매우 큰 collection은 sharding을 고려합니다.
- 각 shard에서 독립적으로 index가 생성되므로 전체 시간이 단축됩니다.
// collection을 shard한 후 index 생성
sh.shardCollection("mydb.collection", { shardKey: 1 });
db.collection.createIndex({ field: 1 });
- shard key를 먼저 정의하고 index를 생성하는 것이 효율적입니다.
점진적 Migration
- 기존 system에서 새 system으로 migration하는 경우, 점진적으로 진행합니다.
- 새 system에 빈 collection을 만들고 index를 먼저 생성합니다.
- 기존 system에서 소량씩 data를 복사합니다.
- application traffic을 점진적으로 새 system으로 전환합니다.
Reference
- https://www.mongodb.com/docs/manual/core/index-creation/
- https://www.mongodb.com/docs/manual/tutorial/build-indexes-on-replica-sets/
- https://www.mongodb.com/docs/manual/reference/method/db.collection.createIndex/
- https://www.mongodb.com/docs/manual/core/index-creation/#comparison-of-index-builds