ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Maxmemory policy
    개발하면서/코드보면서 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에 지우라는 기록을 남기게 합니다.

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


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

    반응형

    댓글

Designed by Tistory.