[MongDB] 인덱스의 expireAfterSeconds 활용으로 collection의 데이터 정리

MongoDB 설정 확인


TTL 모니터 활성화 상태 확인
> db.adminCommand({getParameter: 1, ttlMonitorEnabled: 1});
{ "ttlMonitorEnabled" : true, "ok" : 1 }
false인 경우 활성화 설정 필요
db.adminCommand({setParameter: 1, ttlMonitorEnabled: true});

DB현황 확인


> show dbs
admin            0.000GB
monitor     0.948GB
local            0.000GB
jiraknet        24.964GB

> show dbs
jiraknet        24.964GB
> use jiraknet
switched to db jiraknet
> db.stats()
{
“db” : “jiraknet”,
“collections” : 6,
“views” : 0,
“objects” : 56691366,
“avgObjSize” : 1156.0611780636932,
“dataSize” : 65538687364,
“storageSize” : 22406041600,
“numExtents” : 0,
“indexes” : 22,
“indexSize” : 4399296512,
“ok” : 1
}
db.getCollectionNames()
[ “coll_a”, “coll_b”, “coll_c”, “coll_d”, “coll_e”, “coll_f” ]

collect의 사용량 확인


  • 명령어는 mongdb 콘솔에서 바로 붙여넣기 가능
var collections = db.getCollectionNames();
var stats = [];
collections.forEach(function (n) {
    var s = db.getCollection(n).stats();
    stats.push({
        name: n,
        sizeMB: Math.round(s.size / 1024 / 1024),      // 실제 데이터 크기
        storageMB: Math.round(s.storageSize / 1024 / 1024), // 디스크 점유 크기
        count: s.count
    });
});
사이즈 큰 순서로 정렬후 출력
stats.sort(function(a, b) { return b.storageMB - a.storageMB; });
좀 더 간결하게 출력
print("Collection Name | Storage (MB) | Data (MB) | Documents");
print("-----------------------------------------------------");
stats.forEach(function(s) {
    print(s.name + " | " + s.storageMB + " | " + s.sizeMB + " | " + s.count);
});
결과값 (예시)

{
“name” : “coll_a”,
“sizeMB” : 50778,
“storageMB” : 17840,
“count” : 1537151
},
{
“name” : “coll_b”,
“sizeMB” : 11709,
“storageMB” : 3524,
“count” : 55106786
},

coll_a, coll_b 라는 이름의 컬렉션 사용량이 많아서 정리를 해야 한다고 가정함
두가지 방법
  • 오래된 문서(레코드의 몽고디비식 명칭)를 삭제
  • 인덱스 설정에 TTL을 넣어서 지정한 시간이 지난 문서는 자동으로 삭제
정기적으로 필요한 내용일 수 있으므로 “인덱스 설정” 형태로 진행

인덱스 확인 및 생성


컬렉션의 인덱스 현황 확인
다른 인덱스들도 있지만, 간결한 설명을  위하여 TTL 적용을 위한 “createAt 필드”에 대해서만 설명
> db.coll_a.getIndexes()
    {
        "v" : 2,
        "key" : {
            "createAt" : 1
        },
        "name" : "createAt_1",
        "ns" : "jiraknet.coll_a"
    }

> db.coll_b.getIndexes()
    {
        "v" : 2,
        "key" : {
            "createAt" : 1
        },
        "name" : "createAt_1",
        "ns" : "jiraknet.coll_b"
    }
* 인덱스는 생성되어 있음
* 인덱스로의 역할은 하지만, TTL 설정이 없어 오래된(지정한) 문서의 삭제가 되지 않고 계속 쌓임

기존 인덱스에 별다른 설정이 추가되지 않고, “인덱스 생성”에 대한 기본값으로 생성된 상태라서 “expireAfterSeconds” 옵션이 없음
“expireAfterSeconds” 옵션이 없는 경우 “수정”을 통한 추가 작업이 불가하므로 삭제 후 재생성 진행

db.coll_a.dropIndex("createAt_1")
db.coll_b.dropIndex("createAt_1")

db.coll_a.createIndex({ "createAt": 1 }, { expireAfterSeconds: 15552000, background: true })
db.coll_b.createIndex({ "createAt": 1 }, { expireAfterSeconds: 15552000, background: true })
  • 만료 기한 : 6개월 (15552000)
데이터가 많으면 인덱스 생성 시 부하에 의한 DB운영에 영향 가능성
운영 중인 서버라면 background: true 옵션을 추가하는 것이 안전
(MongoDB 4.2 이상부터는 기본값이 최적화되어 있지만, 명시하는 게 좋다는 의견)
재생성 후 확인
> db.coll_a.getIndexes()
    {
        "v" : 2,
        "key" : {
            "createAt" : 1
        },
        "name" : "createAt_1",
        "ns" : "jiraknet.coll_a",
        "expireAfterSeconds" : 15552000,
        "background" : true
    }

> db.coll_b.getIndexes()
    {
        "v" : 2,
        "key" : {
            "createAt" : 1
        },
        "name" : "createAt_1",
        "ns" : "jiraknet.coll_b",
        "expireAfterSeconds" : 15552000,
        "background" : true
    }

컬렉션의 가장 오래된 데이터가, 내가 설정한 만료 시간에 부합하는지 확인
(MongoDB는 무조건 UTC 기준으로 만료 시간을 계산)

> db.coll_a.find({}, {createAt: 1}).sort({createAt: 1}).limit(1)
{ "_id" : ObjectId("686032521c5dc0439df4f1aa"), "createAt" : ISODate("2025-06-29T03:20:02.107Z") }
> new Date()
ISODate("2025-12-26T03:19:04.669Z")

> db.coll_b.find({}, {createAt: 1}).sort({createAt: 1}).limit(1)
{ "_id" : ObjectId("686032531c5dc043bbfc0bb8"), "createAt" : ISODate("2025-06-29T03:20:03.217Z") }
> new Date()
ISODate("2025-12-26T03:20:14.244Z")
찾아낸 마지막 데이터(문서)의 생성시간이
“new Date()” 로 찾아낸 현재 기준으로 만료시간(expireAfterSeconds)이 도과하지 않았고
일정한 시간이 지난뒤 삭제될것임
즉, 이전 데이터는 모두 삭제가 되었음을 추정할 수 있음
위에서 설명한 “collect 의 사용량 확인” 방법을 다시 사용하면
아래와 같이 모두 정리되었음을 확인 할 수 있음

        {
“name” : “coll_a”,
“sizeMB” : 6580,
“storageMB” : 2035,
“count” : 155847
},
{
“name” : “coll_b”,
“sizeMB” : 1015,
“storageMB” : 304,
“count” : 4741050
},

보통은 여기까지 작업하고 문서는 삭제가 되었으나
디스크 사용량은 그대로이기 때문에, compact 명령어를 이용해서 공간회수를 해야함
> db.runCommand({ compact: 'coll_a' })
{ "ok" : 1 }
> db.runCommand({ compact: 'coll_b' })
{ "ok" : 1 }
디스크 확인시, 사용량이 줄어든것을 확인할 수 있음

작업 완료 후 고찰


TTL 인덱스 생성이 완료되면,

MongoDB 내부의 백그라운드 스레드(TTL Monitor)가 주기적으로 수행하면서 만료된 데이터를 자동으로 삭제
“만료기간으로 지정한 6개월이 지난 데이터가 언제 지워지는지”에 대한 타이밍은

1. 첫 삭제 시점 (즉시 시작)
인덱스 생성이 완료된 직후, TTL 모니터 스레드는 **”이미 6개월이 지난 데이터들(과거 데이터)”**을 발견하고 삭제 작업을 시작합니다.
주의: 만약 지워야 할 과거 데이터가 수백만 건 이상으로 아주 많다면, 한 번에 다 지워지는 게 아니라 서버 성능에 영향을 주지 않도록 조금씩 나누어 지워지므로, 완전히 다 사라질 때까지 수 분에서 수 시간이 걸릴 수 있습니다.

2. 이후 삭제 주기 (60초 간격)
과거 데이터 청소가 다 끝나고 나면, 그 이후부터는 TTL 모니터가 60초(1분)마다 한 번씩 깨어나서 검사를 수행
예: 어떤 로그가 2024년 1월 1일 10:00:00에 생성되었다면, 만료 시간은 2024년 7월 1일 10:00:00
실제 삭제는 이 만료 시간 이후 가장 먼저 도래하는 TTL 모니터 주기(최대 60초 내)에 진행됨

즉, 만료 시점에서 최대 1분 정도의 오차 발생 가능성

요약
과거 데이터: 인덱스 생성 직후부터 삭제 시작 (양이 많으면 시간 좀 걸림)
미래 데이터: 만료 시간(생성일 + 6개월)이 지나면, 1분 이내에 자동으로 삭제됨

About KENNETH 19697 Articles
지락문화예술공작단

Be the first to comment

Leave a Reply

Your email address will not be published.


*


이 사이트는 Akismet을 사용하여 스팸을 줄입니다. 댓글 데이터가 어떻게 처리되는지 알아보세요.