오산돌구 2012. 6. 17. 22:37

http://antirez.com/post/redis-as-LRU-cache.html


http://eli.thegreenplace.net/2009/10/30/handling-out-of-memory-conditions-in-c/


위 두개의 글을 참고한다음에 소스를 보면 더 이해가 쉽습니다. 

(저는 영어가 짧아 굵은글씨만 읽고 바로 소스로;;; 으헝헝~~)


Redis는 메모리 기반 key-value시스템입니다. 메모리는 한정되어있기때문에 무한정 데이타를 저장할수가 없죠.

그래서 Redis에서 나름대로 몇가지 방법을 만들었습니다.



/* Redis maxmemory strategies */
#define REDIS_MAXMEMORY_VOLATILE_LRU 0
#define REDIS_MAXMEMORY_VOLATILE_TTL 1
#define REDIS_MAXMEMORY_VOLATILE_RANDOM 2
#define REDIS_MAXMEMORY_ALLKEYS_LRU 3
#define REDIS_MAXMEMORY_ALLKEYS_RANDOM 4
#define REDIS_MAXMEMORY_NO_EVICTION 5


소스를 보면서 하나씩 알아보도록 해요.

server가 가동되고 client가 접속을 하면 아래와 흐름으로 처리가 됩니다. 


//redis.c  1339 line
if (server.ipfd > 0 && aeCreateFileEvent(server.el,server.ipfd,AE_READABLE,
        acceptTcpHandler,NULL) == AE_ERR) oom("creating file event");
if (server.sofd > 0 && aeCreateFileEvent(server.el,server.sofd,AE_READABLE,
        acceptUnixHandler,NULL) == AE_ERR) oom("creating file event");
//.....
//.....
//network.c
void acceptTcpHandler(......) {
        //....
       acceptCommonHandler(cfd);
}

static void acceptCommonHandler(int fd) {
         //....
          if ((c = createClient(fd)) == NULL) {
}

redisClient *createCleint(int fd) {
          //.....
         if (aeCreateFileEvent(server.el,fd,AE_READABLE,
            readQueryFromClient, c) == AE_ERR)
        {
            close(fd);
            zfree(c);
            return NULL;
        }
}

void readQueryFromClient(....) {
        //....
         processInputBuffer(c);
}

void processInputBuffer(redisClient *c) {
         //.....
        /* Handle the maxmemory directive.
     *
     * First we try to free some memory if possible (if there are volatile
     * keys in the dataset). If there are not the only thing we can do
     * is returning an error. */
    if (server.maxmemory) {
        int retval = freeMemoryIfNeeded();
        if ((c->cmd->flags & REDIS_CMD_DENYOOM) && retval == REDIS_ERR) {
            addReply(c, shared.oomerr);
            return REDIS_OK;
        }
    }
}
으헉 많이도 거치네요. 자세한건 network(select, kqueue, epoll) 살펴볼때 알아보겠습니다. 여기서는 freeMemoryIfNeeded 함수에 대해 알면 되겠네요~~ : )


사전에 있는 데이타를 정리하기전에, AOF와 slave의 buffer들을 정리해줍니다.(AOF, Network 빨리봐야겠네요...)


mem_tofree = mem_used - server.maxmemory;
mem_freed
while ( mem_freed < mem_tofree) {
// 설정한 policy대로 dict의 데이타들을 정리하면서, mem_freed증가
  for (j = 0; j < server.dbnum; j++) {
    if (allkeys policy) 
      dict = server.db[j].dict;
    else
      dict = server.db[j].expires;

     if (random policy)
         // bestkey를 정함
     if (lru policy)
        // bestkey를 정함
     if (ttl policy)
        // bestkey를 정함

      if (bestkey) {
        robj *keyobj = createStringObject(bestkey,sdslen(bestkey));
        propagateExpire(db, keyobj);
        dbDelete(db, keyobj);
        mem_freed += (keyobj지우고 생긴 memory)
      }
  }
}

while(mem_freed < mem_tofree){ } 에서 어떤일을 하는지 알아보겠습니다.


REDIS_MAXMEMORY_ALLKEYS_LRU, REDIS_MAXMEMORY_ALLKEYS_RANDOM인 경우는 실제 dict를

그 외에는 expires를 선택합니다.


Random Policy는 간단하게 dictGetRandomKey 함수로 key를 고르고,


LRU Policy는 maxmemory_samples 갯수 만큼 random하게 키를 고르고 그 키의 value의 시간을 잽니다.

그래서 가장 오래된 key를 bestkey로 선택합니다.


TTL Policy는 expire에 있는 key중에 maxmemory_samples 갯수만큼 임의로 골라서

그 키의 value(expire한 값)를 비교해서 가장 작은값을 bestkey로 선택합니다.


마지막으로 하는 일은, slave에게 bestkey를 삭제하라는 명령과 aof에 지우라는 기록을 남기게 합니다.

그리고 실제 지워진 메모리를 계산을 합니다.


이 동작을 확보하고자 하는 메모리가 될때까지 반복합니다.

반응형