这是我参与11月更文挑战的第17天,活动详情查看:2021最后一次更文挑战
Redis的过期键删除策略
我们知道过期键的删除策略有定时删除、惰性删除和定期删除,redis服务器实际使用的是惰性删除和定期删除
惰性删除
惰性删除策略在db.c的expireIfNeeded
/*
* 如果 key 已经过期,那么将它删除,否则,不做动作。
*
* key 没有过期时间、服务器正在载入或 key 未过期时,返回 0
* key 已过期,那么返回正数值
*/
int expireIfNeeded(redisDb *db, robj *key) {
// 取出 key 的过期时间
long long when = getExpire(db,key);
// key 没有过期时间,直接返回
if (when < 0) return 0; /* No expire for this key */
/* Don't expire anything while loading. It will be done later. */
// 不要在服务器载入数据时执行过期
if (server.loading) return 0;
/* If we are running in the context of a slave, return ASAP:
* the slave key expiration is controlled by the master that will
* send us synthesized DEL operations for expired keys.
*
* Still we try to return the right information to the caller,
* that is, 0 if we think the key should be still valid, 1 if
* we think the key is expired at this time. */
// 如果服务器作为附属节点运行,那么直接返回
// 因为附属节点的过期是由主节点通过发送 DEL 命令来删除的
// 不必自主删除
if (server.masterhost != NULL) {
// 返回一个理论上正确的值,但不执行实际的删除操作
return mstime() > when;
}
/* Return when this key has not expired */
// 未过期
if (mstime() <= when) return 0;
/* Delete the key */
server.stat_expiredkeys++;
// 传播过期命令
propagateExpire(db,key);
// 从数据库中删除 key
return dbDelete(db,key);
}
所有读写数据库的redis命令在执行之前都会调用expireIfNeeded函数对输入键进行检查,如果输入键已经过期,那么expireIfNeeded函数将输入键从数据库中删除,如果输入键未过期,不删除键
定期删除
定期删除策略在redis,c的activeExpireCycle中,当redis的服务器周期性操作redis.c的serverCron函数的时候调用activeExpireCycle,在规定时间内分多次遍历服务器中的各个数据库,从数据库的expires字典中随机检查一部分键的过期时间,并删除其中的过期键
每次运行时,都从一定数量的数据库中取出一定数量的随机键进行检查,并删除其中的过期键
全局变量current_db会记录当前activeExpireCycle函数检查的进度,并在下一次activeExpireCycle函数调用时,接着上一次进度进行处理
随着activeExpireCycle的不断执行,服务器的所有数据都会被检查一遍,这时函数将current_db变量重置为0,然后再次进行新一轮的检查