Redis深度歷險-過期刪除
保存過期時間
Redis支持通過
TTL和PTTL命令來查詢剩下的存活時間
typedef struct redisDb {
......
dict *expires; /* Timeout of keys with a timeout set */
......
} redisDb;
在Redis中每一個數(shù)據(jù)庫中有一個專門存儲過期時間的字典,不管是通過什么命令設(shè)置的過期時間,內(nèi)部存儲的都是過期的毫秒數(shù)時間戳
Redis的過期刪除策略
常用的刪除策略是惰性刪除和定時刪除,Redis是兩者結(jié)合
惰性刪除
在
db.c/expireIfNeeded函數(shù)就是用來處理過期鍵,在命令真正執(zhí)行之前會過濾刪除掉過期的鍵
int expireIfNeeded(redisDb *db, robj *key) {
//如果鍵沒有過期,返回0
if (!keyIsExpired(db,key)) return 0;
//非主機(jī)或者pause狀態(tài)則不會進(jìn)行刪除
if (server.masterhost != NULL) return 1;
if (checkClientPauseTimeoutAndReturnIfPaused()) return 1;
//lazyfree_lazy_expire配置決定同步刪除還是異步刪除
if (server.lazyfree_lazy_expire) {
dbAsyncDelete(db,key);
} else {
dbSyncDelete(db,key);
}
server.stat_expiredkeys++;
propagateExpire(db,key,server.lazyfree_lazy_expire);
notifyKeyspaceEvent(NOTIFY_EXPIRED,
"expired",key,db->id);
signalModifiedKey(NULL,db,key);
return 1;
}
long long getExpire(redisDb *db, robj *key) {
dictEntry *de;
//如果key沒有過期時間返回-1
if (dictSize(db->expires) == 0 ||
(de = dictFind(db->expires,key->ptr)) == NULL) return -1;
//字符串轉(zhuǎn)換為整數(shù)
serverAssertWithInfo(NULL,key,dictFind(db->dict,key->ptr) != NULL);
return dictGetSignedIntegerVal(de);
}
在各種命令中最終都會調(diào)用到此函數(shù)中,邏輯上只是從expire中查詢并比較
定時刪除策略
定時刪除的邏輯實現(xiàn)在
server.c/activeExpireCycle中,在Redis的定時函數(shù)server.c/serverCron中被調(diào)用
void activeExpireCycle(int type) {
......
//記錄此次操作的是哪個數(shù)據(jù)庫
static unsigned int current_db = 0; /* Next DB to test. */
//遍歷16個數(shù)據(jù)庫,對每一個都刪除一定的過期key
for (j = 0; j < dbs_per_call && timelimit_exit == 0; j++) {
.......
定時刪除的代碼比較復(fù)雜,這里簡述一下定時刪除的規(guī)則:
- 每一次定時刪除都會對嘗試所有的數(shù)據(jù)庫進(jìn)行刪除操作
- Redis的定時刪除操作是有時間限制的,超過時間會退出此函數(shù)
- Redis會記錄當(dāng)前過期的操作進(jìn)度,包括操作的數(shù)據(jù)庫、操作的過期鍵游標(biāo)
時間事件
??Redis是基于多路復(fù)用的事件模型,是支持實現(xiàn)定時器的;但是Redis中的時間事件是使用一個順序鏈表存儲的,不適用于大量的定時任務(wù)處理
主備模式的刪除策略
在主備的部署模式下,備機(jī)不會刪除過期鍵;但是給客戶端的返回值還是盡量保持一致,視作已過期刪除