Redis的内存淘汰策略决定了当内存达到限制时,Redis将如何选择删除键来释放空间。以下是Redis支持的几种内存淘汰策略:
volatile-lru
: 只对设置了过期时间的键进行LRU(最近最少使用)淘汰。allkeys-lru
: 对所有键进行LRU淘汰。volatile-lfu
: 只对设置了过期时间的键进行LFU(最少使用频率)淘汰。allkeys-lfu
: 对所有键进行LFU淘汰。volatile-random
: 随机删除设置了过期时间的键。allkeys-random
: 随机删除任意键。volatile-ttl
: 删除即将过期的键。noeviction
: 不删除任何键,如果内存不足,返回错误。
源码解析和算法细节
Redis的内存淘汰算法实现位于evict.c
文件中。以下是一个简化版的LRU算法的源码示例。
LRU算法
Redis的LRU算法不是一个完全精确的LRU,它是通过一个近似值来实现的,称为“样本化LRU”。Redis从数据集中随机抽取一定数量的键,然后从这些样本中淘汰掉最近最少使用的键。
/* evict.c */
#define EVICT_POOL_SIZE 16 /* 样本池大小 */
/* 一个样本池数据结构,存储键和它们的上次访问时间 */
typedef struct {
robj *key;
unsigned long long idle; /* 键的闲置时间 */
} evictionPoolEntry;
evictionPoolEntry eviction_pool[EVICT_POOL_SIZE];
/* ... */
/* 样本化LRU淘汰算法函数 */
void evictionPoolPopulate(int dbid, dict *sampledict, dict *keydict) {
/* ... */
while(count--) {
/* 从数据集中随机选取一个键 */
de = dictGetRandomKey(sampledict);
/* 计算这个键的闲置时间 */
idle = estimateObjectIdleTime(de->val);
/* 检查当前键是否应该加入样本池 */
/* ... */
}
/* ... */
}
在实践中,Redis使用了一个叫做maxmemory-samples
的配置,来决定在进行键淘汰时应该检查多少个键样本。
LFU算法
LFU(最少使用频率)算法是在 Redis 4.0 版本中引入的。它基于“计数器”,该计数器随着键被访问的次数增加而增加,但随着时间的推移而衰减,从而让Redis可以根据键被访问的频率来进行淘汰。
/* evict.c */
/* LFU淘汰算法的一部分代码示例 */
void updateLFU(robj *val) {
unsigned long counter = LFULogIncr(val->lru);
val->lru = LFULogDecr(counter);
}
/* ... */
代码演示
以下代码演示如何在 Redis 配置文件中设置内存淘汰策略:
# redis.conf
maxmemory 2gb
maxmemory-policy allkeys-lru
maxmemory-samples 5
以上配置将内存限制为 2GB,并设置所有键的LRU淘汰策略,同时指定 Redis 在淘汰过程中每次检查5个键样本。
在实际中,选择哪种内存淘汰策略将取决于特定应用程序的需求和访问模式。例如,如果应用程序的缓存使用模式接近LRU,那么allkeys-lru
可能是一个好的选择;而对于访问模式更均匀的数据集,使用allkeys-random
可能更合适。