몽고DB(MongoDB) 소멸 주기(Time to Live, TTL) Java 예시

    몽고DB(MongoDB)는 자체적으로 소멸 주기 혹은 생명주기라고 하는 TTL(Time to Live) 인덱스 기능이 내장되어 있습니다. 예를 들어 로그 데이터와 같이 일반적인 데이터와 달리 중요도가 적은 경우 Info, Warning, Error 처럼 중요도에 따라 데이터 자동 삭제 시기를 설정할 수 있고, 혹은 특정 주기마다 무조건 삭제를 할 수 있는 기능을 제공합니다.

     

    [MongoDB] 몽고DB 소멸 주기(Time to Live, TTL) Java 예시


    TTL의 구현방법

    TTL은 날짜값에 Index를 걸고, expireAfter 속성을 추가적으로 걸면 됩니다.

     

    데이터의 날짜값이 각각 아래와 같다고 가정을 해보겠습니다.

    A-> 2022년 9월 5일 오후 5시 40분
    B-> 2022년 9월 6일 오전 10시 10분
    C-> 2022년 9월 5일 오후 7시 20분

     

    그리고  현재 시간이 2022년 9월 5일 오후 9시 27분이라고 가정한다면, expireAfter 속성을 걸게 된 경우 A,C의 값을 삭제할 수 있게 됩니다. 다만 초까지 모두 정확하게 지켜서 삭제를 하지는 못하고, 삭제는 몽고DB 내부의 루프타임이 있어서 약 60초 후에 삭제가 됩니다.

     

    만료된 문서를 제거하는 백그라운드 작업은 60초마다 실행됩니다 . 결과적으로 문서는 문서 만료와 백그라운드 작업 실행 사이의 기간 동안 컬렉션에 남아 있을 수 있습니다. MongoDB는 색인이 완료된 후 0~60초 후에 문서 삭제를 시작합니다.

    https://www.mongodb.com/docs/manual/core/index-ttl/

     

     

    주기별 삭제 예시

    주기별 삭제는 말그대로 데이터가 들어간 후, 일정 주기마다 데이터를 삭제하는 것을 말합니다. 예를 들어, 데이터가 저장 된 후 1시간 후에 삭제를 시키는 등의 작업을 해야 할 경우, 아래의 예제를 참고하시면 됩니다.

     

    아래의 예시는 데이터 저장 후, 5분 후에 삭제를 하는 예시입니다.

     

    TtlService.java

    package com.tistory.needjarvis.service;
    
    import com.tistory.needjarvis.dao.TtlDAO;
    
    /**
     * Time to live service
     *
     * @author Steele
     * @since 2022.09.06
     */
    public class TtlService {
    
        public void insert() {
            TtlDAO ttlDAO = new TtlDAO();
            long resultCount = ttlDAO.setTestData();
            System.out.println("insert count->" + resultCount);
        }
    }

     

    TtlDAO.java

    package com.tistory.needjarvis.dao;
    
    import com.mongodb.*;
    import java.util.Date;
    
    
    /**
     * Time to live 설정
     *
     * @author Steele
     * @since 2022.09.06
     */
    public class TtlDAO {
    
        final String dbNm = "test";
    
        public long setTestData() {
            long count = 0;
            String collectionNm = "ttl";
    
            try {
                MongoClient mongo = new MongoClient("localhost", 27017);
                DB db = mongo.getDB(dbNm);
    
                DBCollection collection = db.getCollection(collectionNm);
    
                DBObject keyObj = new BasicDBObject();
                keyObj.put("createdAt", 1);
    
                DBObject valueObj = new BasicDBObject();
                valueObj.put("expireAfterSeconds", 300);    // 5분
    
                collection.createIndex(keyObj, valueObj);
                DBObject item = createTtlData();
                collection.insert(item);
            } catch(Exception e) {
                e.printStackTrace();
                return -1;
            }
    
            return count;
        }
    
    
        private DBObject createTtlData() {
            BasicDBObjectBuilder docBuilder = BasicDBObjectBuilder.start();
    
            docBuilder.append("title", "[World Now] 성서에나 나올 홍수 파키스탄 비상사태");
            docBuilder.append("contents", "홍수로 불어난 물길 한가운데 있는 바위에 한 소년이 고립됐습니다. " +
                    "                               거센 물살이 금방이라도 덮칠 듯 위협하는 상황. 파키스탄 군용 헬기가 접근해 이...");
            docBuilder.append("createdAt", new Date());
    
            return docBuilder.get();
        }
    }

     

     

    위의 로직을 보면 기존 몽고DB의 insert와 다를바 없지만, 한가지 인덱스가 차이가 나는 것을 알 수 있고, 데이터에 new Date()로 현재 날짜값을 저장하였습니다.

     

    DBObject keyObj = new BasicDBObject();
    keyObj.put("createdAt", 1);
    
    DBObject valueObj = new BasicDBObject();
    valueObj.put("expireAfterSeconds", 300);    // 5분

     

    즉 데이터의 날짜값은 현재 시간으로 저장을 하기 때문에, 데이터가 저장이 되자마자 만료가 되며, 날짜값에 인덱스를 걸었는데 expireAfterSeconds 키에 300이라는 값을 지정하고 색인을 걸었습니다. 해석을 하자면 날짜값이 만료가 될 경우(new Date를 하니 데이터가 저장되자마자 만료가 됩니다) 300초 후에 삭제를 하겠다라는 것이 됩니다.

     

    데이터를 저장한 후 1시간 후에 삭제를 원할 경우, expireAfterSeconds, 3600으로 하면 1시간 후에 삭제가 하는 것이 됩니다.

     

     

    코드 실행

    위의 코드를 실행하면, 아래와 같이 데이터가 저장이 된 후

     

     

     

    데이터가 저장 된 후

     

    약 5분 후에 아래와 같이 데이터가 없어진 것을 알 수 있습니다.

    데이터가 삭제 된 모습

     

     

    특정 시간 삭제

    이번에는 특정 시간이 되면 삭제를 하는 것을 만들어 보겠습니다.

     

    TtlDAO.java

    package com.tistory.needjarvis.dao;
    
    import com.mongodb.*;
    
    import java.time.LocalDateTime;
    import java.time.ZoneId;
    import java.util.Date;
    
    
    /**
     * Time to live 설정
     *
     * @author Steele
     * @since 2022.09.06
     */
    public class TtlDAO {
    
        final String dbNm = "test";
    
        public long setTestData() {
            long count = 0;
            String collectionNm = "ttl";
    
            try {
                MongoClient mongo = new MongoClient("localhost", 27017);
                DB db = mongo.getDB(dbNm);
    
                DBCollection collection = db.getCollection(collectionNm);
    
                DBObject keyObj = new BasicDBObject();
                keyObj.put("createdAt", 1);
    
                DBObject valueObj = new BasicDBObject();
                valueObj.put("expireAfterSeconds", 0);    // 즉시 삭제
    
                collection.createIndex(keyObj, valueObj);
    
                for(int i = 0; i < 5; i++) {
                    DBObject item = createTtlData(i, i+1);
                    collection.insert(item);
                }
            } catch(Exception e) {
                e.printStackTrace();
                return -1;
            }
    
            return count;
        }
    
    
        private DBObject createTtlData(int no, int minute) {
            BasicDBObjectBuilder docBuilder = BasicDBObjectBuilder.start();
    
            docBuilder.append("no", no);
            docBuilder.append("title", "[World Now] 성서에나 나올 홍수 파키스탄 비상사태");
            docBuilder.append("contents", "홍수로 불어난 물길 한가운데 있는 바위에 한 소년이 고립됐습니다. " +
                    "                               거센 물살이 금방이라도 덮칠 듯 위협하는 상황. 파키스탄 군용 헬기가 접근해 이...");
            docBuilder.append("createdAt", toDate(LocalDateTime.now().plusMinutes(minute)));
    
            return docBuilder.get();
        }
    
    
        private Date toDate(LocalDateTime ldt) {
            return Date.from(ldt.atZone(ZoneId.systemDefault()).toInstant());
        }
    }

     

    테스트용 데이터에 no 컬럼을 추가하였고, createdAt 컬럼에는 현재값보다 1분씩 더 뒤에 있는 값으로 데이터를 저장하였습니다.

     

    코드실행

    위의 코드를 실행하면 아래와 같이 5개의 데이터가 저장되었으며, no는 0~4번까지 그리고 createdAt의 값이 1분씩 차이가 나는 것을 확인할 수 있습니다.

     

    1분씩 추가된 데이터

     

    데이터를 넣은 후, 1분이 지나게 되면

     

    no가 0인 데이터가 삭제된 모습

     

    위와 같이 createdAt이 만료된 것들이 하나씩 삭제가 되는 것을 확인 할 수 있습니다. 

     

     

    댓글

    Designed by JB FACTORY