본문 바로가기

Technical/DBMS

mongodb ttl collection 사용에 관하여(pymongo 추가)

 

[mongodb client UI]


MongoDB 2.2.x 2.1.2 부터 TTL(Time To Live) 기능이 제공되고 있다. IT에 익숙한 분이라면 icmp, DNS, cache server 등의 프로토콜이나 설정/작동 방법상에서 자주 나타나는 용어라는 것을 알 수 있으리라.


mongodb에서의 이 기능은 단순하게 설명하자면, 지정된 시간 이전의 레코드를 자동으로 지우는(purging, removing) 기능이다. 주로 통계성 데이터나 모니터링 데이터 등 시계열 데이터가 저장되어 있을 경우나 일정 시간 동안만 저장되어야 하는 session 정보 등을 저장하는데 유용하게 사용할 수 있겠다.

중요한 주의사항 몇 가지를 아래에 정리해 두고 테스트를 진행해 본다.
  1. mongodb의 Date 에 해당하는 날짜시간 데이터가 저장되는 필드가 필요하고, 여기에 ensureIndex로 인덱스를 두는 방식이다
  2. 해당 필드는 다른 index 에 복합적으로 참조되지 않아야 한다(단일 필드, 단일 인덱스)
  3. 해당 필드에는 위 1번에서 처럼 Date BSON type의 데이터가 UTC 기준으로 저장되어야 자동으로 지워진다
  4. mongodb의 ttl thread는 1분단위로 동작한다. 즉 10초로 설정한다고 데이터 저장 후 10초만에 지워지는 것이 아니라 1분 가량의 오차가 존재하며, DB 시스템의 부하 정도(mongod의 workload가 얼마나 큰지)에 따라 달라질 수 있다는 즉, 정확히 해당 시간 경과후 지워진다는 보장은 없다는 것.
  5. 모든 컬렉션의 공통 필드인 _id 필드는 ttl 필드로 사용할 수 없다.
  6. capped collection은 ttl index를 적용할 수 없다.

각설하고, 아래와 같이 간단히 테스트 해 본다

> db.tt_col.save( { id: 'id1', name: 'aaa', dt: new Date() } );
> db.tt_col.save( { id: 'id2', name: 'bbb', dt: new Date() } );
> db.tt_col.save( { id: 'id3', name: 'ccc', dt: new Date() } );
> db.tt_col.find();
{ "_id" : ObjectId("509de58b1493b98702441310"), "id" : "id1", "name" : "aaa", "dt" : ISODate("2012-11-10T05:26:35.370Z") }
{ "_id" : ObjectId("509de5ac1493b98702441311"), "id" : "id2", "name" : "bbb", "dt" : ISODate("2012-11-10T05:27:08.218Z") }
{ "_id" : ObjectId("509de5ba1493b98702441312"), "id" : "id3", "name" : "ccc", "dt" : ISODate("2012-11-10T05:27:22.450Z") }
> db.tt_col.ensureIndex( {dt: 1}, {expireAfterSeconds: 10} );
> db.tt_col.find();
{ "_id" : ObjectId("509de58b1493b98702441310"), "id" : "id1", "name" : "aaa", "dt" : ISODate("2012-11-10T05:26:35.370Z") }
{ "_id" : ObjectId("509de5ac1493b98702441311"), "id" : "id2", "name" : "bbb", "dt" : ISODate("2012-11-10T05:27:08.218Z") }
{ "_id" : ObjectId("509de5ba1493b98702441312"), "id" : "id3", "name" : "ccc", "dt" : ISODate("2012-11-10T05:27:22.450Z") }
잘 보면 날짜/시간 부분이 localtime이 아닌 UTC 기준시간으로 저장되어 있음에 유의한다.

약 40초 후에 컬렉션의 데이터를 조회해 보니 3건의 데이터가 삭제되어 있음을 확인할 수 있다. 
> db.tt_col.find().count();
0
> db.tt_col.find();
>


응용할 수 있는 트릭이라면, 지워지게 하고 싶지 않은 데이터가 있을 경우 어떨게 하면 될까?


한 가지 방법은 보관이 필요한 데이터를 찾아서 ttl 설정 필드 값을 Date() 로 update하는 것이다. 만약 계속 보존처리를 해야 한다면 매번 update를 수행해야 할 것이고,

계속 보존해야 한다면, 단순하게도 다음과 같이 ttl 필드를 Date가 아닌 다른 데이터 값으로 설정하면 된다.

> db.tt_col.update( { id: 'id1' }, { dt: null } );


[pymongo 2.3]

사용 방법은 유사하지만 python 내에서 ttl 기능 설정/사용시에 유의할 사항이 있다. 아래 예시의 함정을 찾아 보자.


Python 2.7.2 (default, Aug 19 2011, 20:41:43) [GCC] on linux2

Type "help", "copyright", "credits" or "license" for more information.

>>> import pymongo

>>> from datetime import datetime, date, time, timedelta

>>> datetime.now()

datetime.datetime(2012, 11, 2, 2, 3, 50, 18750)

>>> pyMongoConnStr = 'mongodb://10.10.10.70:28017/myschema'

>>> connMongo = pymongo.Connection( pyMongoConnStr )

>>> connMongo.myschema.tt_col.save( { 'a':1, 'b':2, 'dt': datetime.now() } )
ObjectId('50aa68a94422527639f91a34')
>>> connMongo.monitor.t_col.ensure_index( 'dt', pymongo.ASCENDING, expireAfterSeconds = 15 )
>>> connMongo.myschema.tt_col.find_one()
{u'a': 1, u'dt': datetime.datetime(2012, 11, 2, 2, 13, 13, 517000), u'_id': ObjectId('50aa68a94422527639f91a34'), u'b': 2}


15초 가량 후에 해당 row가 삭제되어야 하는데, 10분이 지나도 멀쩡하게 남아 있다. index 설정을 봐도 잘못 된게 없고, 시스템 시간도 정확하게 현재시간을 가리키고 있는데, ...


결론을 말하자면, 위에서 등록한 row는 한국 시간으로 9시간 가량 후에 지워질 것이다. 왜냐하면 mongodb의 ttl thread 는 UTC 를 기준으로 작동하는데, python에서 mongodb에 등록한 데이터(row)에서 dt 필드의 값인 datetime.now() 는 KST(대한민국표준시; 또는 시스템 시간이 localtime) 기준의 시간 값이기 때문이다. 즉 python에서 ttl index 필드의 시간 값을 설정할 때는 반드시 datetime.utcnow() 를 사용하여야만 한다. 이점 유의하자.



- Barracuda -