Redis内存淘汰策略

118 阅读2分钟

Redis的内存淘汰策略决定了当内存达到限制时,Redis将如何选择删除键来释放空间。以下是Redis支持的几种内存淘汰策略:

  1. volatile-lru: 只对设置了过期时间的键进行LRU(最近最少使用)淘汰。
  2. allkeys-lru: 对所有键进行LRU淘汰。
  3. volatile-lfu: 只对设置了过期时间的键进行LFU(最少使用频率)淘汰。
  4. allkeys-lfu: 对所有键进行LFU淘汰。
  5. volatile-random: 随机删除设置了过期时间的键。
  6. allkeys-random: 随机删除任意键。
  7. volatile-ttl: 删除即将过期的键。
  8. 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可能更合适。