一、Redis过期键存储:
1惰性删除策略:
所有的redis命令在执行之前都会调用expireIfNeeded函数。
主库,expireIfNeeded会删除过期键。
从库,expireIfNeeded会返回一个逻辑删除的结果,但是物理删除由主库的synthesized DEL operations控制。
2.定时删除策略:
我们线上的机器为1milliseconds执行调用一次activeExpireCycle。
if (server->masterhost == NULL) activeExpireCycle();
主库,会定时删除过期键。采用策略:从一定数量的数据库的过期库中取出一定数量的随机键进行检查,不为空则删除。不保证实时删除。(具体策略见后面的代码)
综上
主库的过期键删除策略有两种:1.在执行所有操作之前调用expireIfNeeded—惰性删除。2. 1milliseconds执行调用一次activeExpireCycle,每次随机删除部分键—定时删除。
dictDelete(db->dict,key->ptr); //删除dict库中过期键数据,同时dict库中的总量减一
answer:
Ttl命令执行的是
if (expireIfNeeded(db,key))return NULL;
exists命令执行的是:
expireIfNeeded(db,key);
也就是说
主库中调用expireIfNeeded,过期键都会被删除,返回结果一致。
附函数代码:
int expireIfNeeded(redisDb *db, robj *key) {
long long when = getExpire(db,key); //获取过期时间
if (when < 0) return 0; //未设置过期键
......
/* 从库直接返回过期逻辑值,不做dbDelete*/
if (server->masterhost != NULL) {
return mstime() > when;
}
if (mstime() <= when) return 0; //设置过期健但未过期
/* Delete the key */
server->stat_expiredkeys++;
propagateExpire(db,key);
return dbDelete(db,key);
}
/* Try to expire a few timed out keys. The algorithm used is adaptive and
* will use few CPU cycles if there are few expiring keys, otherwise
* it will get more aggressive to avoid that too much memory is used by
* keys that can be removed from the keyspace. */
//自适应算法 并不保证删除所有的key
void activeExpireCycle(void) {
int j;
long long start = mstime();
//轮询所有的数据库
for (j = 0; j < server->dbnum; j++) {
int expired, iteration = 0;
redisDb *db = server->db+j;
/* Continue to expire if at the end of the cycle more than 25%
* of the keys were expired. */
do {
//每个数据库轮询的次数为min(dictSize,10)
long num = dictSize(db->expires);
long long now = mstime();
expired = 0;
if (num > REDIS_EXPIRELOOKUPS_PER_CRON)
num = REDIS_EXPIRELOOKUPS_PER_CRON;
while (num--) {
dictEntry *de;
long long t;
if ((de = dictGetRandomKey(db->expires)) == NULL) break;
t = (long long) dictGetEntryVal(de);
if (now > t) {
sds key = dictGetEntryKey(de);
robj *keyobj = createStringObject(key,sdslen(key));
propagateExpire(db,keyobj);
dbDelete(db,keyobj);
decrRefCount(keyobj);
expired++;
server->stat_expiredkeys++;
}
}
/* 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 & 0xff) == 0 && /* Check once every 255 iterations */
(mstime()-start) > REDIS_EXPIRELOOKUPS_TIME_LIMIT) return;//超时退出
} while (expired > REDIS_EXPIRELOOKUPS_PER_CRON/4);//过期键数量大于2才会继续删除
}
}