概述
Redis 作为一种高性能的内存数据库,提供了数据过期和淘汰策略以管理存储的数据。本文将详细探讨 Redis 中数据失效的基本原理、实现方式,并结合源码进行分析,帮助读者更深入地理解和优化 Redis 的使用。
数据过期机制
过期键的存储方式
Redis 中每个数据库都有一个 expires
字典,用于保存设置了过期时间的键及其对应的过期时间戳。这个字典的作用类似于索引,通过它能够快速判断某个键是否已过期。
typedef struct redisDb {
dict *dict; // 存储键值对
dict *expires; // 存储键的过期时间
// 其他成员...
} redisDb;
定时删除 vs 惰性删除 vs 定期删除
定时删除(Active Expiration / Timer-based Deletion)
定时删除是一种“主动式”删除机制。当用户为一个键设置过期时间时,Redis 会记录该键的过期时间戳,并注册一个定时器,在指定时间到达后自动触发删除操作。虽然这种方法能及时释放内存资源,但可能会导致 CPU 使用率飙升。
// 设置键的过期时间
void setExpire(client *c, robj *key, long long when) {
dictEntry *de;
de = dictFind(c->db->dict,key->ptr);
serverAssertWithInfo(c,key,de != NULL);
dictSetVal(c->db->expires,dictAddRaw(c->db->expires,key->ptr,&de),when);
}
惰性删除(Lazy Expiration / On-access Deletion)
惰性删除是一种“被动式”机制。只有在访问某个键时才会检查其是否已经过期,如果已过期则立即删除。这种方式可以显著降低 CPU 的消耗,但代价是部分过期键会长时间保留在内存中。
int expireIfNeeded(redisDb *db, robj *key) {
if (!keyIsInHashtable(db->expires, key)) return 0;
mstime_t when = getExpire(db, key);
mstime_t now = mstime();
if (now > when) {
deleteExpiredKey(db, key);
return 1;
}
return 0;
}
定期删除(Periodic Expiration)
定期删除是 Redis 主动清理过期键的主要方式之一。Redis 在后台每隔一段时间扫描部分设置了过期时间的键,随机选取一部分进行过期检测,并删除其中已经过期的键。
void activeExpireCycle(int type) {
static unsigned int current_db = 0;
unsigned int j, iteration = 0;
unsigned int dbs_per_call = CRON_DBS_PER_CALL;
int start = ustime(), timelimit, noexpire = 0;
if (dbs_per_call > server.dbnum) dbs_per_call = server.dbnum;
timelimit = 1000000 * ACTIVE_EXPIRE_CYCLE_SLOW_TIME_PERC / 100;
timelimit -= (ustime() - start);
for (j = 0; j < dbs_per_call && timelimit > 0 && !noexpire; j++) {
redisDb *db = server.db + (current_db % server.dbnum);
current_db++;
int num = dictSize(db->expires);
int slots = ceil(num / 16.0); // 随机选择 slot 数量
while (slots--) {
dictEntry *de;
long long ttl;
if ((de = dictGetRandomKey(db->expires)) == NULL) break;
robj *key = dictGetKey(de);
ttl = dictGetSignedIntegerVal(de) - mstime();
if (ttl < 0) {
dbDelete(db, key);
}
}
if ((iteration++ % 10) == 0) {
long long elapsed = ustime() - start;
if (elapsed >= timelimit) break;
}
}
}
内存淘汰机制
当 Redis 使用的内存超过 maxmemory
限制时,将会根据配置的淘汰策略来决定移除哪些键值对。常见的淘汰策略包括:
volatile-lru
:从设置了过期时间的数据集中挑选最近最少使用的数据淘汰。allkeys-lru
:从所有数据集中挑选最近最少使用的数据淘汰。volatile-random
:从设置了过期时间的数据集中任意选择数据淘汰。allkeys-random
:从所有数据集中任意选择数据淘汰。
UML图解 Redis 过期键处理流程
以下是使用 UML 绘制的简化版 Redis 过期键处理流程图,包括定时删除、惰性删除和定期删除的工作流程:
定时删除:
惰性删除:
定期删除:
结语
Redis 通过一系列复杂的机制来管理数据的生命周期和内存使用情况,旨在提供高效的数据管理和查询服务。理解这些基本概念和机制对于优化性能、避免内存溢出等问题至关重要。希望这篇文章能为你提供有价值的参考信息,帮助你更好地利用 Redis 提供的功能。