
MongoDB 공간 사용량, 왜 헷갈릴까?
MongoDB를 처음 접하는 분들에게 디스크 공간 사용량은 다소 혼란스럽게 느껴질 수 있습니다. 이 글에서는 MongoDB가 디스크 공간을 어떻게 할당하는지 설명하고, ObjectRocket 대시보드에 표시되는 공간 사용량 정보를 해석하는 방법을 통해 인스턴스를 압축(compaction)해야 하는 시점과 샤드를 추가해야 하는 시점을 판단하는 기준을 알려드립니다.
먼저 5GB 단일 샤드로 구성된 새로운 Medium 인스턴스를 예로 들어 보겠습니다. "ocean"이라는 데이터베이스에 테스트 데이터를 입력하고, 일부러 테스트 데이터셋 대비 크기가 상당히 큰 인덱스 몇 개를 추가한 뒤의 공간 사용량 화면은 다음과 같습니다.

315MiB 데이터인데 왜 2.1GiB를 사용 중일까?
315MiB의 데이터와 254MiB의 인덱스만 있는데 5GiB 샤드 중 2.1GiB를 사용 중이라고 표시되는 이유가 궁금하실 것입니다. 이를 이해하려면 MongoDB가 디스크에 데이터를 저장하는 방식, 즉 '익스텐트(extent)'라는 단위로 데이터를 저장한다는 점부터 살펴봐야 합니다.
ObjectRocket 인스턴스는 smallfiles 옵션으로 실행되므로 첫 번째 익스텐트는 16MB로 할당됩니다. 이후 익스텐트 크기는 512MB에 도달할 때까지 두 배씩 커지고, 그 이후에는 모든 익스텐트가 512MB 파일로 할당됩니다. 따라서 예제의 "ocean" 데이터베이스 파일 구조는 다음과 같습니다.
$ ls -lh ocean/
total 1.5G
-rw------- 1 mongodb mongodb 16M Aug 20 22:30 ocean.0
-rw------- 1 mongodb mongodb 32M Aug 20 20:44 ocean.1
-rw------- 1 mongodb mongodb 64M Aug 20 22:23 ocean.2
-rw------- 1 mongodb mongodb 128M Aug 20 22:30 ocean.3
-rw------- 1 mongodb mongodb 256M Aug 20 22:30 ocean.4
-rw------- 1 mongodb mongodb 512M Aug 20 22:30 ocean.5
-rw------- 1 mongodb mongodb 512M Aug 20 22:30 ocean.6
-rw------- 1 mongodb mongodb 16M Aug 20 22:30 ocean.ns
drwxr-xr-x 2 mongodb mongodb 4.0K Aug 20 22:30 _tmp익스텐트와 조각화의 발생 원리
이 익스텐트들은 데이터베이스의 데이터와 인덱스를 모두 저장합니다. MongoDB에서는 익스텐트에 데이터가 한 번이라도 기록되면 다음 논리적 익스텐트가 즉시 할당됩니다. 따라서 위 구조에서 ocean.6에는 아직 데이터가 없겠지만, ocean.5가 가득 차는 순간을 대비해 미리 할당된 상태입니다. ocean.6에 데이터가 기록되는 즉시 새로운 512MB 익스텐트인 ocean.7이 다시 미리 할당됩니다.
MongoDB에서는 데이터를 삭제해도 해당 공간이 압축(compaction)을 실행하기 전까지는 반환되지 않습니다. 그래서 시간이 지나면서 데이터 삭제(또는 새로운 키 추가 등으로 문서가 기존 저장 위치보다 커져 이동하는 경우)로 인해 데이터 파일이 조각화(fragmentation)될 수 있습니다. 압축 작업은 복제 셋(replica set)의 다른 멤버로부터 데이터를 복제해 데이터 파일을 처음부터 다시 생성하는 방식으로 진행되며, 이 과정에서 파일 조각화가 해소됩니다.
네임스페이스 파일과 시스템 데이터베이스
추가로 16MB짜리 파일 하나가 네임스페이스(namespace)를 저장하는데, 바로 ocean.ns 파일입니다. MongoDB 인스턴스의 모든 데이터베이스마다 동일한 패턴이 반복됩니다. "ocean" 데이터베이스 외에도 샤드에는 "admin"과 "local"이라는 두 개의 시스템 데이터베이스가 존재합니다.
"admin" 데이터베이스는 모든 데이터베이스 사용자의 정보를 저장합니다(2.6.x 이전 버전에서는 관리자 사용자 전용으로 사용되었습니다). 크기가 작더라도 이 데이터베이스 역시 16MB 익스텐트 하나, 미리 할당된 32MB 익스텐트 하나, 그리고 16MB 네임스페이스 파일을 차지합니다.
오플로그(oplog)와 local 데이터베이스
두 번째 시스템 데이터베이스는 "local"입니다. ObjectRocket에서 제공하는 각 샤드는 3개 멤버로 구성된 복제 셋입니다. 복제본 간 동기화를 유지하기 위해 MongoDB는 모든 업데이트를 oplog라는 로그에 기록합니다. oplog는 각 복제본에서 동기화되며, 세컨더리 복제본에 적용해야 할 변경 사항을 추적하는 데 사용됩니다. 이 oplog는 "local" 데이터베이스 안에 capped collection 형태로 존재합니다. ObjectRocket에서는 oplog 크기를 일반적으로 샤드 크기의 10%로 설정하는데, 5GB 샤드의 경우 oplog가 500MB로 설정됩니다. 따라서 "local" 데이터베이스는 16MB 익스텐트 하나, 512MB 익스텐트 하나, 16MB 네임스페이스 파일로 구성됩니다.
저널(journal): 빠른 쓰기를 위한 이중 기록
마지막으로 예제 샤드에는 저널(journal)이라는 관리 영역이 하나 더 있습니다. 저널은 약 128MB 크기의 파일 1~3개로 구성됩니다. 쓰기 작업이 발생하면 MongoDB는 먼저 해당 업데이트를 저널에 순차적으로 기록합니다. 그런 다음 백그라운드 스레드가 주기적으로(통상 60초에 한 번) 이 변경 사항을 실제 데이터 파일(앞서 언급한 익스텐트)에 플러시(flush)합니다.
이렇게 이중으로 기록하는 이유는, 실제 데이터 파일에 쓸 때 필요한 탐색(seek) 작업보다 저널에 순차적으로 쓰는 편이 훨씬 빠르기 때문입니다. 변경 사항을 즉시 저널에 기록함으로써 MongoDB는 모든 쓰기 작업이 데이터 파일 기록 완료를 기다리지 않고도 장애 발생 시 데이터 복구를 보장할 수 있습니다. 현재 프라이머리 복제본에서는 두 개의 저널 파일이 활성 상태로 확인됩니다.
$ ls -lh journal/
total 273M
-rw------- 1 mongodb mongodb 149M Aug 20 22:26 j._1
-rw------- 1 mongodb mongodb 124M Aug 20 22:30 j._2MongoDB는 업데이트 빈도와 백그라운드 플러시 빈도에 따라 이 파일들을 자동으로 로테이션합니다.
대시보드 지표 해석하기
여기까지 MongoDB가 디스크 공간을 사용하는 방식을 살펴봤으니, 이제 앞서 보여드린 ObjectRocket 대시보드의 공간 사용량 막대가 각각 무엇을 의미하는지 알아보겠습니다.
- NS 값, 48MB — 앞서 언급한 세 데이터베이스(ocean, admin, local)의 16MB 네임스페이스 파일 3개를 합한 값입니다.
- Data 값, 315MiB — 모든 데이터베이스(시스템 데이터베이스 포함)에 대한
db.stats()의 dataSize 합계입니다. - Index 값, 253.9MiB — 모든 데이터베이스(시스템 데이터베이스 포함)에 대한
db.stats()의 indexSize 합계입니다. - Storage 값, 687.2MiB — 모든 데이터베이스의 데이터와 인덱스 합계에, 삭제 후 회수되지 못한 공간을 더한 값입니다.
- Total File 값, 2.0 GiB — 프라이머리 복제본에서 실제로 사용 중인 전체 디스크 용량입니다. Storage 값과 NS 값이 포괄하는 공간 외에 미리 할당된 익스텐트도 포함되지만, 저널이 사용하는 공간은 포함되지 않습니다.
조각화율과 전체 사용률 계산하기
이러한 지표들을 바탕으로 간단한 계산만으로 인스턴스가 압축이 필요할 만큼 조각화되었는지 판단할 수 있습니다. 조각화로 인해 손실된 것으로 추정되는 공간 비율은 다음과 같이 계산합니다.
100% – (Data + Indexes) / Storage
예제 인스턴스의 경우 17%입니다(100% – (315MiB Data + 253.9MiB Index) / 687.2MiB Storage = 17%). 조각화율이 20%에 근접하면 인스턴스 압축을 실행하는 것을 권장합니다.
또 하나 확인할 수 있는 지표는 전체 공간 사용률을 기준으로 한 샤드 추가 필요 여부입니다. 전체 공간 사용률은 다음과 같이 계산합니다.
(Total File / (플랜 용량 × 샤드 수)) × 100%
예제 인스턴스의 경우 40%입니다((2 GiB / 5 GiB × 1 샤드) × 100% = 40%). 전체 공간 사용률이 80%에 근접하면 샤드 추가를 권장합니다. 사용률이 80%에 도달하는 것으로 확인되면 지원팀에 문의하시면 인스턴스에 샤드를 추가하는 작업을 도와드립니다.