redis有哪些数据淘汰策略

本文介绍了Redis在内存数据集大小达到一定程度时实行的数据淘汰策略,包括volatile - lru、allkey - lru等多种策略。还阐述了LRU和TTL数据淘汰机制,指出都是从键值对中随机挑选合适的淘汰。此外,提到Redis服务器执行命令时会检测内存,超额则进行数据淘汰。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

redis内存数据数据集大小升到一定大的时候,就会实行数据淘汰策略(回收策略)。

1,volatile-lru:从已设置过期时间的哈希表(server.db[i].expires)中随机挑选多个key,然后在选到的key中用lru算法淘汰最近最少使用的数据

2,allkey-lru:从所有key的哈希表(server.db[i].dict)中随机挑选多个key,然后再选到的key中利用lru算法淘汰最近最少使用的数据

3,volatile-ttl:从已设置过期时间的哈希表(server.db[i].expires)中随机挑选多个key,然后在选到的key中选择过期时间最小的数据淘汰掉。

4,volatile-random:从已设置过期时间的哈希表(server.db[i].expires)中随机挑选key淘汰掉。

5,allkey-random:从所有的key的哈希表(server.db[i].dict)中随机挑数据淘汰

6,no-eviction(驱逐):内存达到上限,不淘汰数据。

 

redis确认驱逐某个键值对后,会删除这个数据,并将这个数据变更消息发布到本地(AOF持久化)和从机(主从连接)。

 LRU数据淘汰机制是这样的:在数据集中随机挑选几个键值对,去除其中最近最少使用的键值对淘汰。所以Redis并不是保证取得所有数据集中最少最少使用的键值对,而只是在随机挑选的几个键值对中。

TTL数据淘汰机制:从国企时间redisDB.expires表中随机挑选几个键值对,取出其中最快过期的键值对淘汰。所以Redis并不保证取得所有过期时间表中最快过期的键值对,而是随机挑选的几个键值对中。

无论是什么机制,都是从所有的键值对中挑选合适的淘汰。

在哪里开始淘汰数据

Redis服务器每执行一次命令的时候,会检测使用的内存是否超额。如果超额,即进行数据淘汰。

int freeMemoryIfNeeded(void) {
    /**
     * noeviction 不淘汰数据,什么都不做
     */
    if (server.maxmemory_policy == MAXMEMORY_NO_EVICTION)
        return C_ERR;
    while (mem_freed < mem_tofree) {
        int j, k, keys_freed = 0;
        for (j = 0; j < server.dbnum; j++) {
            /**
             * 选择操作的哈希表,Redis另外维护着一个保存过期时间的key=>expire关联的哈希表
             */
            if (server.maxmemory_policy == MAXMEMORY_ALLKEYS_LRU ||
                server.maxmemory_policy == MAXMEMORY_ALLKEYS_RANDOM)
            {
                dict = server.db[j].dict;
            } else {
                dict = server.db[j].expires;
            }
            /**
             * 分支一:全局哈希表随机或者过期时间哈希表中,随机淘汰一个key
             */
            if (server.maxmemory_policy == MAXMEMORY_ALLKEYS_RANDOM ||
                server.maxmemory_policy == MAXMEMORY_VOLATILE_RANDOM)
            {
                de = dictGetRandomKey(dict);
                bestkey = dictGetKey(de);
            }
            /**
             * 分支二:全局哈希表随机或者过期时间哈希表中,随机采样多个数据,再运用lru算法挑选一个淘汰
             */
            else if (server.maxmemory_policy == MAXMEMORY_ALLKEYS_LRU ||
                server.maxmemory_policy == MAXMEMORY_VOLATILE_LRU)
            {
                /* 样本集 */
                struct evictionPoolEntry *pool = db->eviction_pool;
                while(bestkey == NULL) {
                    /* 
                     * 采样,更新和维护样本集;
                     * 样本集开始是空的,每次操作完并不会清空样本集;
                     * 而且每次采样,都会采集多个数据,同时和样本集中已有的数据进行比较,新增或者更新样本集;
                     */ 
                    evictionPoolPopulate(dict, db->dict, db->eviction_pool);
                    /**
                     * 开始对样本集使用lru算法,淘汰样本集中访问时间最晚的key
                     */
                    for (k = MAXMEMORY_EVICTION_POOL_SIZE-1; k >= 0; k--) {
                        if (pool[k].key == NULL) continue;
                        de = dictFind(dict,pool[k].key);
                        /* 把选取到的key从样本集中移除 */
                        sdsfree(pool[k].key);
                        memmove(pool+k,pool+k+1,
                            sizeof(pool[0])*(MAXMEMORY_EVICTION_POOL_SIZE-k-1));
                        pool[MAXMEMORY_EVICTION_POOL_SIZE-1].key = NULL;
                        pool[MAXMEMORY_EVICTION_POOL_SIZE-1].idle = 0;
                        /* pool样本集内的key,只是样本,不一定和db内保持一致,也不必,可能在db中已经被删除的,所以要作判断 */
                        if (de) {
                            bestkey = dictGetKey(de);
                            break;
                        } else {
                            /* Ghost... */
                            continue;
                        }
                    }
                }
            }
            /**
             * 分支三:在设置了过期时间的哈希表里面随机选择多个key,在挑选到的key中选择过期时间最小的一个淘汰掉
             */
            else if (server.maxmemory_policy == MAXMEMORY_VOLATILE_TTL) {
                for (k = 0; k < server.maxmemory_samples; k++) {
                    sds thiskey;
                    long thisval;
                    de = dictGetRandomKey(dict);
                    thiskey = dictGetKey(de);
                    thisval = (long) dictGetVal(de);
                    if (bestkey == NULL || thisval < bestval) {
                        bestkey = thiskey;
                        bestval = thisval;
                    }
                }
            }
            if (bestkey) {
                long long delta;
                robj *keyobj = createStringObject(bestkey,sdslen(bestkey));
                // 命令扩散,把删除key的命令同步到所有从库slave
                propagateExpire(db,keyobj);
                // 删除key
                dbDelete(db,keyobj);
            }
        }
    }
    return C_OK;
}

 

### Redis 的过期策略和淘汰策略 #### 过期策略 Redis 的过期策略主要用于处理设置了生存时间(TTL)的键。当一个键被设置为在某个时间点后自动删除时,Redis 并不会立即删除该键,而是采用惰性删除和定期删除两种机制来管理这些过期键。 - **惰性删除**:只有当客户端尝试访问一个键时,Redis 才会检查该键是否已过期。如果已过期,则删除该键[^1]。 - **定期删除**:Redis 会在后台周期性地检查数据库中是否存在过期键,并主动删除它们。这种机制可以减少内存占用,但也会增加 CPU 负载。定期删除的频率和每次删除的键数量由 Redis 配置决定[^1]。 #### 淘汰策略 Redis淘汰策略用于解决内存不足的问题。当 Redis 内存达到最大限制时,它会根据配置的淘汰策略选择一些键进行删除,以释放内存空间。常见的淘汰策略包括: - **LRU (Least Recently Used)**:删除最近最少使用的键。此策略假设最近未使用的键在未来也不太可能被使用[^2]。 - **LFU (Least Frequently Used)**:删除最不常使用的键。此策略基于访问频率,认为访问频率低的键在未来也不太可能被频繁使用[^3]。 - **TTL (Time To Live)**:删除剩余生存时间最短的键。此策略适用于那些设置了较短生存时间的键[^4]。 - **Random**:随机删除键。此策略简单粗暴,但可能导致重要数据被删除[^5]。 #### 使用场景 不同的淘汰策略适用于不同的使用场景: - **LRU**:适合缓存场景,尤其是当数据访问模式具有明显的局部性时,例如网页缓存、API 响应缓存等[^2]。 - **LFU**:适合需要长期保存某些数据的场景,例如分布式系统中的元数据存储,其中某些数据可能长时间未被访问但仍然重要[^3]。 - **TTL**:适合短期任务或临时数据存储,例如会话管理、验证码存储等[^4]。 - **Random**:适合对数据丢失敏感度较低的场景,例如日志存储、统计分析等[^5]。 #### 示例代码 以下是一个简单的 Redis 配置示例,展示如何设置淘汰策略: ```bash # 设置 Redis 的最大内存限制 maxmemory 100mb # 设置淘汰策略为 LRU maxmemory-policy allkeys-lru ``` #### 注意事项 - 在生产环境中,建议根据实际需求选择合适的淘汰策略,并监控 Redis 的内存使用情况。 - 对于关键数据,避免使用容易被淘汰的策略,如 Random 或 TTL。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值