-
Expires개발하면서/코드보면서 2012. 8. 21. 00:05반응형
http://redis.io/commands/expire
위 링크와 소스를 보고 이해했습니다.
expireGenericCommand 함수에서 설정을합니다.
expire는 마스터에서만 직접 이뤄집니다.
그리고 (설정시간이 현재보다 이전시간이고, AOF를 로딩하지않고, 마스터일때의 동작)과 (그렇지 않을때의 동작,)
두가지 branch가 나옵니다.첫번째의 경우는, 이미 시간이 지나간 것이니 삭제를 수행합니다. (networking.c rewriteClientCommandVector 따로 포스팅 해야겠다...)
두번째의 경우는 redisDb의 expires dict에 key를 저장하고 signalModifiedKey를 수행합니다.
Redis에서는 Optimistic Lock을 사용합니다. get / set 연산에 대해서 lock이 필요하겠죠~
1. val = [get kang]
2. val += 10
3. [set kang val]
2번일때 kang 에 대해서 다른 명령어가 온다던가, 다른 클라이언트에서 kang 에 대해 다른 연산을 할 경우
원치 않는 결과가 나옵니다. sequence를 보장하고 싶다면 watch~!를 이용합니다.
Redis의 transaction 은 다른 transaction과 마찬가지로 '모 아니면 도' 로 실행합니다.
즉~!! signalModifiedKey 함수는 해당 키에 대해 '수정했음' 표시라고 생각하시면 됩니다.
(다른 클라이언트에 대해서는 무효임ㅎㅎ)
100마디 말보다 1장의 스샷 ㅋㅋㅋ(redis의 transaction에 대해서는 , transactions)
REDIS_HZ(100ms)마다 serverCron을 수행합니다. 어떻게 100ms마다 동작하는지는
http://redis.io/topics/internals-rediseventlib 를 참고하면서 따로 포스팅을 하려고 합니다. : )
일단은 serverCron이 100ms마다 수행이 되는구나.....라고 가정하고 어떻게 expire를 체크하는 지 알아보겠습니다
실제 expire를 실행하는 함수는 redis.c : activeExpireCycle 에서 수행합니다.
if (server.masterhost == NULL) activeExpireCycle(); //master인 경우에만 수행
expire하는데 다음과 같은 정책이 있습니다.
* expire의 dict 의 실제 사용량과(dictht의 used) 할당된 양과(dictht의 size)의 비율이 1%라면 break~!!!
(servercron 에서 tryResizeHashTables로 검사)* do-while의 loop당 최대 10개의 expire key만 검사합니다. (성능이슈로인해)
* do-while 16번 수행될때마다 시간검사를 해서 timelimit 이상의 시간이 수행되면 loop종료 (이놈도 성능이슈)
( timelimit = 1000000*REDIS_EXPIRELOOKUPS_TIME_PERC/REDIS_HZ/100; 는 왜 하는지 이해가 안되는데...)* do-while loop 한번에 REDIS_EXPIRELOOKUPS_PER_CRON/4 개의 키도 못 지웠다면 loop 종료
위에 정책을 말로 풀어보자면,
100ms가 지나서 activeExpireCycle 수행
start = ustime() if (used와 size비율을 봐서 1%미만) 종료~!! do { { 임의의 key 10개 가져와서, 시간된(?ㅋㅋ) key는 삭제; expired++} iteration++ if (iteration == 16 && 지금시간-start > timelimit) 종료 } while( expired > REDIS_EXPIRELOOKUPS_PER_CRON/4)
삐딱하게보자면, expire가져올때 임의로 가져오는데 expire가 아닌 한참 남은 키만 계속 가져오면 아웃~!!
2000개정도의 key에 대해 동일한 TTL값을 지정했을때 동시에 지워지지 않는다~!! 뭐 이정도 생각해볼 수 있겠네요.
실제 소스입니다
void activeExpireCycle (void) { long long start = ustime(); timelimit = 1000000*REDIS_EXPIRELOOKUPS_TIME_PERC/REDIS_HZ/100; if (timelimit <= 0) timelimit = 1; for ( 0...server.dbnum) { do { now = mstime(); num = expires dict의 used; slots = expires dict의 size; if ( num*100 / slots < 1) break; if ( num > 10) num = 10; while (num--) { if ((de = dictGetRandomKey(db->expires)) == NULL) break; t = dictGetSignedIntegerVal(de); if (now > t) { sds key = dictGetKey(de); robj *keyobj = createStringObject(key,sdslen(key)); propagateExpire(db,keyobj); dbDelete(db,keyobj); decrRefCount(keyobj); expired++; server.stat_expiredkeys++; } } if ((iteration & 0xf) == 0 && /* check once every 16 cycles. */ (ustime()-start) > timelimit) return; } while(expired > REDIS_EXPIRELOOKUPS_PER_CRON/4); } //end for( 0...server.dbnum) }
반응형