-
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에 지우라는 기록을 남기게 합니다.
그리고 실제 지워진 메모리를 계산을 합니다.
이 동작을 확보하고자 하는 메모리가 될때까지 반복합니다.
반응형