redis代码 数据超时实现
生活随笔
收集整理的這篇文章主要介紹了
redis代码 数据超时实现
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
redis的超時處理
1) 當再次訪問該數據時, 發現該數據已超時過期, 則刪掉; 返回給調用者為空。(被動發現)
2) redis server起來之后, 會注冊定時器事件(每毫秒觸發1次超時), 在該定時器處理函數中, 輪流各db;大致策略是從該db.expired dict中:
-----a. 嘗試取20(ACTIVE_EXPIRE_CYCLE_LOOKUPS_PER_LOOP)次隨機key,判斷是否有過期的,過期的處理掉。 如果該db中過期的不足5個(ACTIVE_EXPIRE_CYCLE_LOOKUPS_PER_LOOP/4),則不再處理該db、轉而處理下一個db。
-----b. 執行a 16的整數倍次。如果此時發現本次處理時間已太長(>25ms), 則不再處理。否則繼續a 16次。typedef struct redisDb {dict *dict;???????????????? /* The keyspace for this DB */dict *expires;????????????? /* Timeout of keys with a timeout set */如果數據設置了超時,則dict和expires中都會放一份。dict *blocking_keys;??????? /* Keys with clients waiting for data (BLPOP) */dict *ready_keys;?????????? /* Blocked keys that received a PUSH */dict *watched_keys;???????? /* WATCHED keys for MULTI/EXEC CAS */int id;long long avg_ttl;????????? /* Average TTL, just for stats */數據近似平均過期時間
} redisDb;struct redisServer {/* General */char *configfile;?????????? /* Absolute config file path, or NULL */int hz;???????????????????? /* serverCron() calls frequency in hertz */redisDb *db;dict *commands;???????????? /* Command table */dict *orig_commands;??????? /* Command table before command renaming. */aeEventLoop *el; unsigned lruclock:22;?????? /* Clock incrementing every minute, for LRU */分鐘級別的超時設置。unsigned lruclock_padding:10;int shutdown_asap;????????? /* SHUTDOWN needed ASAP */....
};#define REDIS_LRU_CLOCK_MAX ((1<<21)-1) /* Max value of obj->lru */
#define REDIS_LRU_CLOCK_RESOLUTION 10 /* LRU clock resolution in seconds */
typedef struct redisObject {unsigned type:4;unsigned notused:2; /* Not used */unsigned encoding:4;unsigned lru:22; /* lru time (relative to server.lruclock) */ lru = server.lruclockint refcount;void *ptr;
} robj;每次訪問該key時,即更新該key對應的value的最近訪問時間為server.lruclock。 db.c:lookupKey()。
在讀或者寫時, 都會調用lookupKey更新。int expireIfNeeded(redisDb *db, robj *key) {mstime_t when = getExpire(db,key);//得到設置的截止超時時間。 絕對時間。未設置超時則為-1。....不符合超時條件,退出; 底下會處理超時。/* Delete the key */server.stat_expiredkeys++;propagateExpire(db,key);//將本次數據超時信息傳遞給slave、寫AOFnotifyKeyspaceEvent(REDIS_NOTIFY_EXPIRED, "expired",key,db->id);//將本次超時信息廣播給相關訂閱者。?return dbDelete(db,key);//從db.expires和db.dict中刪除掉。
}robj *lookupKey(redisDb *db, robj *key) {dictEntry *de = dictFind(db->dict,key->ptr);if (de) {robj *val = dictGetVal(de);/* Update the access time for the ageing algorithm.* Don't do it if we have a saving child, as this will trigger* a copy on write madness. */if (server.rdb_child_pid == -1 && server.aof_child_pid == -1)val->lru = server.lruclock;return val;} else {return NULL;}
}
robj *lookupKeyRead(redisDb *db, robj *key) {robj *val;expireIfNeeded(db,key); //判斷該key是否超時、超時就都淘汰--->底下的讀就會失敗、因為已經被淘汰了。val = lookupKey(db,key); //if (val == NULL)server.stat_keyspace_misses++;elseserver.stat_keyspace_hits++;return val;
}redis.c:initServer中,增加定時器事件執行serverCron函數:
1) 該函數更新server.lruclock = (server.unixtime/REDIS_LRU_CLOCK_RESOLUTION) & REDIS_LRU_CLOCK_MAX;//server.unixtime =是通過time(NULL)得到的。
即server.lruclock 是當前時間/10 后結果的低21位。
2) 執行databasesCron。該函數負責處理key的超時、rehash、resize。 其中key超時函數activeExpireCyclevoid activeExpireCycle(int type) {/* This function has some global state in order to continue the work* incrementally across calls. */static unsigned int current_db = 0; /* Last DB tested. */從上次訪問的db的下一個db接著開始處理static int timelimit_exit = 0;????? /* Time limit hit in previous call? */static long long last_fast_cycle = 0; /* When last fast cycle ran. */unsigned int j, iteration = 0;unsigned int dbs_per_call = REDIS_DBCRON_DBS_PER_CALL;long long start = ustime(), timelimit;if (type == ACTIVE_EXPIRE_CYCLE_FAST) {/* Don't start a fast cycle if the previous cycle did not exited* for time limt. Also don't repeat a fast cycle for the same period* as the fast cycle total duration itself. */ //該變量為靜態變量。上次函數未退出時, timelimit_exit為0; 1則退出。//由于本函數是由定時器事件觸發,所以某些情況下可能有多個本函數在執行。??????? if (!timelimit_exit) return;
if (start < last_fast_cycle + ACTIVE_EXPIRE_CYCLE_FAST_DURATION*2) return;
last_fast_cycle = start;
}
/* We usually should test REDIS_DBCRON_DBS_PER_CALL per iteration, with
* two exceptions:
*
* 1) Don't test more DBs than we have.
* 2) If last time we hit the time limit, we want to scan all DBs
* in this iteration, as there is work to do in some DB and we don't want
* expired keys to use memory for too much time. */
if (dbs_per_call > server.dbnum || timelimit_exit)
dbs_per_call = server.dbnum;
/* We can use at max ACTIVE_EXPIRE_CYCLE_SLOW_TIME_PERC percentage of CPU time
* per iteration. Since this function gets called with a frequency of
* server.hz times per second, the following is the max amount of
* microseconds we can spend in this function. */
//ACTIVE_EXPIRE_CYCLE_SLOW_TIME_PERC 25.
//server.hz default值是 10. 那么timelimit約為25000. (與ustime比, 那么就是25ms)
timelimit = 1000000*ACTIVE_EXPIRE_CYCLE_SLOW_TIME_PERC/server.hz/100;//??? timelimit_exit = 0;??? if (timelimit <= 0) timelimit = 1;??? if (type == ACTIVE_EXPIRE_CYCLE_FAST)??????? timelimit = ACTIVE_EXPIRE_CYCLE_FAST_DURATION; /*1000 in microseconds. */??? for (j = 0; j < dbs_per_call; j++) {//dbs_per_call數據庫個數。??????? int expired;??????? redisDb *db = server.db+(current_db % server.dbnum);//逐個db輪流處理。 每個db只處理??????? /* Increment the DB now so we are sure if we run out of time???????? * in the current DB we'll restart from the next. This allows to???????? * distribute the time evenly across DBs. */??????? current_db++;??????? /* Continue to expire if at the end of the cycle more than 25%???????? * of the keys were expired. */??????? do {??????????? unsigned long num, slots;??????????? long long now, ttl_sum;??????????? int ttl_samples;??????????? /* If there is nothing to expire try next DB ASAP. */??????????? if ((num = dictSize(db->expires)) == 0) {??????????????? db->avg_ttl = 0;??????????????? break;??????????? }??????????? slots = dictSlots(db->expires);??????????? now = mstime();??????????? /* When there are less than 1% filled slots getting random???????????? * keys is expensive, so stop here waiting for better times...???????????? * The dictionary will be resized asap. */??????????? if (num && slots > DICT_HT_INITIAL_SIZE &&??????????????? (num*100/slots < 1)) break;??????????? /* The main collection cycle. Sample random keys among keys???????????? * with an expire set, checking for expired ones. */??????????? expired = 0;??????????? ttl_sum = 0;??????????? ttl_samples = 0;??????????? if (num > ACTIVE_EXPIRE_CYCLE_LOOKUPS_PER_LOOP)??????????????? num = ACTIVE_EXPIRE_CYCLE_LOOKUPS_PER_LOOP;//20。。。。??????????? while (num--) {??????????????? dictEntry *de;??????????????? long long ttl;??????????????? if ((de = dictGetRandomKey(db->expires)) == NULL) break;//取db->expires的隨機桶的首元素??????????????? ttl = dictGetSignedIntegerVal(de)-now;??????????????? if (activeExpireCycleTryExpire(db,de,now)) expired++;//如果已經超時,則從db刪掉、增加server.stat_expiredkeys??????????????? if (ttl < 0) ttl = 0;??????????????? ttl_sum += ttl;??????????????? ttl_samples++;??????????? }??????????? /* Update the average TTL stats for this database. */??????????? if (ttl_samples) {??????????????? long long avg_ttl = ttl_sum/ttl_samples;??????????????? if (db->avg_ttl == 0) db->avg_ttl = avg_ttl;??????????????? /* Smooth the value averaging with the previous one. */??????????????? db->avg_ttl = (db->avg_ttl+avg_ttl)/2;??????????? }??????????? /* We can't block forever here even if there are many keys to???????????? * expire. So after a given amount of milliseconds return to the???????????? * caller waiting for the other active expire cycle. */??????????? iteration++;??????????? if ((iteration & 0xf) == 0 && /* check once every 16 iterations. */??????????????? (ustime()-start) > timelimit)??????????? {??????????????? timelimit_exit = 1;??????????? }??????????? if (timelimit_exit) return;??????????? /* We don't repeat the cycle if there are less than 25% of keys???????????? * found expired in the current DB. */??????? } while (expired > ACTIVE_EXPIRE_CYCLE_LOOKUPS_PER_LOOP/4);??? }//end for(int i=.., i < dbnum...)} 《新程序員》:云原生和全面數字化實踐50位技術專家共同創作,文字、視頻、音頻交互閱讀
if (start < last_fast_cycle + ACTIVE_EXPIRE_CYCLE_FAST_DURATION*2) return;
last_fast_cycle = start;
}
/* We usually should test REDIS_DBCRON_DBS_PER_CALL per iteration, with
* two exceptions:
*
* 1) Don't test more DBs than we have.
* 2) If last time we hit the time limit, we want to scan all DBs
* in this iteration, as there is work to do in some DB and we don't want
* expired keys to use memory for too much time. */
if (dbs_per_call > server.dbnum || timelimit_exit)
dbs_per_call = server.dbnum;
/* We can use at max ACTIVE_EXPIRE_CYCLE_SLOW_TIME_PERC percentage of CPU time
* per iteration. Since this function gets called with a frequency of
* server.hz times per second, the following is the max amount of
* microseconds we can spend in this function. */
//ACTIVE_EXPIRE_CYCLE_SLOW_TIME_PERC 25.
//server.hz default值是 10. 那么timelimit約為25000. (與ustime比, 那么就是25ms)
timelimit = 1000000*ACTIVE_EXPIRE_CYCLE_SLOW_TIME_PERC/server.hz/100;//??? timelimit_exit = 0;??? if (timelimit <= 0) timelimit = 1;??? if (type == ACTIVE_EXPIRE_CYCLE_FAST)??????? timelimit = ACTIVE_EXPIRE_CYCLE_FAST_DURATION; /*1000 in microseconds. */??? for (j = 0; j < dbs_per_call; j++) {//dbs_per_call數據庫個數。??????? int expired;??????? redisDb *db = server.db+(current_db % server.dbnum);//逐個db輪流處理。 每個db只處理??????? /* Increment the DB now so we are sure if we run out of time???????? * in the current DB we'll restart from the next. This allows to???????? * distribute the time evenly across DBs. */??????? current_db++;??????? /* Continue to expire if at the end of the cycle more than 25%???????? * of the keys were expired. */??????? do {??????????? unsigned long num, slots;??????????? long long now, ttl_sum;??????????? int ttl_samples;??????????? /* If there is nothing to expire try next DB ASAP. */??????????? if ((num = dictSize(db->expires)) == 0) {??????????????? db->avg_ttl = 0;??????????????? break;??????????? }??????????? slots = dictSlots(db->expires);??????????? now = mstime();??????????? /* When there are less than 1% filled slots getting random???????????? * keys is expensive, so stop here waiting for better times...???????????? * The dictionary will be resized asap. */??????????? if (num && slots > DICT_HT_INITIAL_SIZE &&??????????????? (num*100/slots < 1)) break;??????????? /* The main collection cycle. Sample random keys among keys???????????? * with an expire set, checking for expired ones. */??????????? expired = 0;??????????? ttl_sum = 0;??????????? ttl_samples = 0;??????????? if (num > ACTIVE_EXPIRE_CYCLE_LOOKUPS_PER_LOOP)??????????????? num = ACTIVE_EXPIRE_CYCLE_LOOKUPS_PER_LOOP;//20。。。。??????????? while (num--) {??????????????? dictEntry *de;??????????????? long long ttl;??????????????? if ((de = dictGetRandomKey(db->expires)) == NULL) break;//取db->expires的隨機桶的首元素??????????????? ttl = dictGetSignedIntegerVal(de)-now;??????????????? if (activeExpireCycleTryExpire(db,de,now)) expired++;//如果已經超時,則從db刪掉、增加server.stat_expiredkeys??????????????? if (ttl < 0) ttl = 0;??????????????? ttl_sum += ttl;??????????????? ttl_samples++;??????????? }??????????? /* Update the average TTL stats for this database. */??????????? if (ttl_samples) {??????????????? long long avg_ttl = ttl_sum/ttl_samples;??????????????? if (db->avg_ttl == 0) db->avg_ttl = avg_ttl;??????????????? /* Smooth the value averaging with the previous one. */??????????????? db->avg_ttl = (db->avg_ttl+avg_ttl)/2;??????????? }??????????? /* We can't block forever here even if there are many keys to???????????? * expire. So after a given amount of milliseconds return to the???????????? * caller waiting for the other active expire cycle. */??????????? iteration++;??????????? if ((iteration & 0xf) == 0 && /* check once every 16 iterations. */??????????????? (ustime()-start) > timelimit)??????????? {??????????????? timelimit_exit = 1;??????????? }??????????? if (timelimit_exit) return;??????????? /* We don't repeat the cycle if there are less than 25% of keys???????????? * found expired in the current DB. */??????? } while (expired > ACTIVE_EXPIRE_CYCLE_LOOKUPS_PER_LOOP/4);??? }//end for(int i=.., i < dbnum...)} 《新程序員》:云原生和全面數字化實踐50位技術專家共同創作,文字、視頻、音頻交互閱讀
總結
以上是生活随笔為你收集整理的redis代码 数据超时实现的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: skiplist
- 下一篇: cache + db 性能