返回顶部
首页 > 资讯 > 数据库 >关于redis Key淘汰策略的实现方法
  • 531
分享到

关于redis Key淘汰策略的实现方法

策略方法redis 2022-06-04 17:06:23 531人浏览 泡泡鱼
摘要

1 配置文件中的最大内存删除策略 在Redis的配置文件中,可以设置redis内存使用的最大值,当redis使用内存达到最大值时(如何知道已达到最大值?),redis会根据配置文件中的策略选取要删除的key

1 配置文件中的最大内存删除策略

Redis的配置文件中,可以设置redis内存使用的最大值,当redis使用内存达到最大值时(如何知道已达到最大值?),redis会根据配置文件中的策略选取要删除的key,并删除这些key-value的值。若根据配置的策略,没有符合策略的key,也就是说内存已经容不下新的key-value了,但此时有不能删除key,那么这时候写的话,将会出现写错误。


1.1 最大内存参数设置

若maxmemory参数设置为0,则分两种情况:

*在64位系统上,表示没有限制。
*在32为系统上,是3G,redis官方文档的说法是,32位系统最大内存是4G,预留1G给系统。而且策略会自动设置为noeviction。

也就是说在32位系统上,若maxmemory设置为0,则默认是3G,当到达3G,再往reidis中写入时,则会报错。


1.2 到达最大内存时的几种删除key的策略

* volatile-lru -> remove the key with an expire set using an LRU alGorithm
按照LRU算法(最少最近没使用)来选取,并删除已经设置了expire时间的key。
* allkeys-lru -> remove any key accordingly to the LRU algorithm
根据LRU算法,删除任意的key。不论这个key是否设置了expire时间。
* volatile-random -> remove a random key with an expire set
随机删除一个设置了expire时间的key。
* allkeys-random -> remove a random key, any key
随机删除任意一个key,不论这个key是否设置了expire时间。
* volatile-ttl -> remove the key with the nearest expire time (minor TTL)
删除具有最近终止时间值(TTL)的key。
* noeviction -> don't expire at all, just return an error on write operations
若没有设置终止时间,返回一个错误。


1.3 配置内存最大值策略

以下这些命令的默认策略是:volatile-lru

# At the date of writing this commands are: set setnx setex append
# incr decr rpush lpush rpushx lpushx linsert lset rpoplpush sadd
# sinter sinterstore suNIOn sunionstore sdiff sdiffstore zadd zincrby
# zunionstore zinterstore hset hsetnx hmset hincrby incrby decrby
# getset mset msetnx exec sort
#
# The default is:
# maxmemory-policy volatile-lru


1.4 配置要删除key的检测样本个数

maxmemory-samples

由于LRU和最小TTL算法都是不是精确的算法。因此你可以选择要检测样本的个数。例如,默认情况下redis将会检查3个key,并从这3个key中选取一个最近没有使用的key。当然你可以修改检查样本的个数的值。

要修改这个值,可以通过在配置文件中设置参数:

maxmemory-samples 3

2 实现

这几种删除策略的实现都是在函数 freeMemoryIfNeeded(void) 中完成的。下面具体讲解每种策略是如何实现的。

2.1 什么时候去删除key-value

当设置了maxmemory-policy策略后,什么时候会去删除key呢?

实际上,当设置了maxmemory参数后,在处理每个命令的时候都会根据maxmemory-policy去删除对应的key值。

代码如下:


// 处理客户端的每个命令,都会调用这个函数
int processCommand(redisClient *c) {
  ... ...
  
  // 以上意思是:若存在可以删除的key,就释放一些内存,若不存在,给客户端返回一个错误。
  if (server.maxmemory) {               // 若maxmemory不为0,则调用以下函数,释放其中一些key
    int retval = freeMemoryIfNeeded();   // 根据配置策略删除key
    if ((c->cmd->flags & REDIS_CMD_DENYOOM) && retval == REDIS_ERR) {  // 若出错,就终止处理命令,把错误返回给客户端
      flagTransaction(c);
      addReply(c, shared.oomerr);
      return REDIS_OK;
    }
  }
  ... ...
}


实战1:若没有设置maxmemory变量,即使设置了maxmemory-policy,也不会起作用。

实战2:若没有设置maxmemory变量,在处理命令时将不会调用释放策略,会加速命令的处理过程。

2.2 删除key的总体流程

当内存达到最大值时需要按策略删除老的key,所有的删除操作和删除策略的实现都是在函数freeMemoryIfNeeded()中实现的。

在执行删除策略之前,先要选取db和查找key。

总体步骤如下:


int freeMemoryIfNeeded(void) {
  size_t mem_used, mem_tofree, mem_freed;
  int slaves = listLength(server.slaves);
  mstime_t latency;


  
  mem_used = zmalloc_used_memory();
  if (slaves) {
    listIter li;
    listnode *ln;


    listRewind(server.slaves,&li);
    while((ln = listNext(&li))) {
      redisClient *slave = listNodeValue(ln);
      unsigned long obuf_bytes = getClientOutputBufferMemoryUsage(slave);
      if (obuf_bytes > mem_used)
        mem_used = 0;
      else
        mem_used -= obuf_bytes;
    }
  }
  if (server.aof_state != REDIS_AOF_OFF) {
    mem_used -= sdslen(server.aof_buf);
    mem_used -= aofRewriteBufferSize();
  }


  
  // 检查目前系统是否超过内存的限制
  if (mem_used <= server.maxmemory) return REDIS_OK;


  if (server.maxmemory_policy == REDIS_MAXMEMORY_NO_EVICTION)
    return REDIS_ERR; 


  
  mem_tofree = mem_used - server.maxmemory;
  mem_freed = 0;
  latencyStartMonitor(latency);
  while (mem_freed < mem_tofree) {
    int j, k, keys_freed = 0;
    // 遍历16个数据库


    for (j = 0; j < server.dbnum; j++) {
      long bestval = 0; 
      sds besTKEy = NULL;
      struct dictEntry *de;
      redisDb *db = server.db+j;
      dict *dict;


      // 这里要注意,若是ALLKEYS_xx策略,则直接在对应库结构的dict中查找key。
      // 若是非ALLKEYS_xx策略,也就是可能是 volatile-xxx等策略,操作的库结构将设置成expires结构。


      if (server.maxmemory_policy == REDIS_MAXMEMORY_ALLKEYS_LRU ||
        server.maxmemory_policy == REDIS_MAXMEMORY_ALLKEYS_RANDOM)
      {
        // 若设置了
        dict = server.db[j].dict;
      } else {
        dict = server.db[j].expires;
      }
      // 若数据库的大小为0,说明没有key存在,继续在下一个数据库中查找
      if (dictSize(dict) == 0) continue;


... ... 


}

2.2 volatile-lru机制和allkeys-lru的实现

2.2.1 redis中的LRU机制

对于LRU机制,redis的官方文档有这样的解释:


Redis LRU algorithm is not an exact implementation. This means that Redis is not able to pick the best candidate for eviction, that is, the access that was accessed the most in the past. Instead it will try to run an approximation of the LRU algorithm, by sampling a small number of keys, and evicting the one that is the best (with the oldest access time) among the sampled keys.
 
However since Redis 3.0 (that is currently in beta) the algorithm was improved to also take a pool of good candidates for eviction. This improved the perfORMance of the algorithm, making it able to approximate more closely the behavior of a real LRU algorithm.
What is important about the Redis LRU algorithm is that you are able to tune the precision of the algorithm by changing the number of samples to check for every eviction. This parameter is controlled by the following configuration directive:

maxmemory-samples 5

The reason why Redis does not use a true LRU implementation is because it costs more memory. However the approximation is virtually equivalent for the application using Redis. The following is a graphical comparison of how the LRU approximation used by Redis compares with true LRU.

大意是说,redis的LRU算法不是正真意思上的LRU。而是使用另外一种方式实现的。也就意味着,redis并不能每次都选择一个最好的key来删除。没有使用正真的LRU算法的原因是,它可能会消耗更多的内存。该算法和正真的LRU算法效果大概相同。

redis是在一小部分key中选择最优的要删除的key。这一小部分key的个数可以指定,可以在配置文件中设置参数maxmemory-samples 。

2.2.2 LRU机制的实现

freeMemoryIfNeeded()函数,首先要计算最大空余内存和目前已经使用的内存大差值,若不够了,就要释放老的key-value。

若使用的是LRU策略,就会走以下代码,先进行最优删除key的选择,然后进行删除操作:


int freeMemoryIfNeeded(void) {
  size_t mem_used, mem_tofree, mem_freed;
  int slaves = listLength(server.slaves);
  mstime_t latency;


  
  mem_used = zmalloc_used_memory(); // 计算目前使用的内存大小,要排除slave和AOF使用的buffer大小
  if (slaves) { //遍历slaves链表,减去slave使用的内存数量
    listIter li;
    listNode *ln;


    listRewind(server.slaves,&li);
    while((ln = listNext(&li))) {
      redisClient *slave = listNodeValue(ln);
      unsigned long obuf_bytes = getClientOutputBufferMemoryUsage(slave);
      if (obuf_bytes > mem_used)
        mem_used = 0;
      else
        mem_used -= obuf_bytes;
    }
  }
  if (server.aof_state != REDIS_AOF_OFF) { //减去AOF使用的内存大小
    mem_used -= sdslen(server.aof_buf);
    mem_used -= aofRewriteBufferSize();
  }


   //检查是否达到设置的内存上限
  if (mem_used <= server.maxmemory) return REDIS_OK;
  // 不释放内存
  if (server.maxmemory_policy == REDIS_MAXMEMORY_NO_EVICTION)
    return REDIS_ERR; 


   //计算要释放的内存量
  mem_tofree = mem_used - server.maxmemory;
  mem_freed = 0;
  latencyStartMonitor(latency);
  while (mem_freed < mem_tofree) { //已经释放的内存小于要释放的内存量
    int j, k, keys_freed = 0;


    for (j = 0; j < server.dbnum; j++) { //遍历所有数据库开始释放内存
      long bestval = 0; 
      sds bestkey = NULL;
      struct dictEntry *de;
      redisDb *db = server.db+j;
      dict *dict;


       // 这一步要先选择淘汰取值的数据库的dict
      if (server.maxmemory_policy == REDIS_MAXMEMORY_ALLKEYS_LRU ||
        server.maxmemory_policy == REDIS_MAXMEMORY_ALLKEYS_RANDOM)
      { //若maxmemory-policy的值是LRU或RANDOM时,直接在主数据库中进行淘汰
        dict = server.db[j].dict;
      } else { // 其他策略,在已经设置了终止时间的key中间进行淘汰。
        dict = server.db[j].expires;
      }
      if (dictSize(dict) == 0) continue; //当前数据库没有数据跳过


       //若是RANDOM策略中的一个
      if (server.maxmemory_policy == REDIS_MAXMEMORY_ALLKEYS_RANDOM ||
        server.maxmemory_policy == REDIS_MAXMEMORY_VOLATILE_RANDOM)
      {
        de = dictGetRandomKey(dict);
        bestkey = dictGetKey(de);
      }


      // 若删除策略是LRU策略中的一个
      else if (server.maxmemory_policy == REDIS_MAXMEMORY_ALLKEYS_LRU ||
        server.maxmemory_policy == REDIS_MAXMEMORY_VOLATILE_LRU)
      {
        // 根据配置文件中maxmemory_samples的值,决定做几次选择,删除的key要从这些key中选出来。
        for (k = 0; k < server.maxmemory_samples; k++) {
          sds thiskey;
          long thisval;
          robj *o;


          // 从库中随机选取一个key-value结构(dictEntry类型)的节点
          de = dictGetRandomKey(dict);
          thiskey = dictGetKey(de); // // 从该节点中获取key的字符串地址
          
          // 若最大内存删除策略是volatile-lru,则需要从db中查找thiskey。
          // 若是VOLATILE-xx策略,则目前操作的库的存储结构是expires,此时需要从dict中找到该key
          if (server.maxmemory_policy == REDIS_MAXMEMORY_VOLATILE_LRU)
            de = dictFind(db->dict, thiskey);
          // 获取key de的value值
          o = dictGetVal(de);
          // 查看该key的剩下的生存时间
          thisval = estimateObjectIdleTime(o);


          
          // 每次都从遍历的几个Key中选出lru最长的key。
          // 那么如何更新key的lru值呢?每次查找该key的时候就会更新该key的lru值,该值是系统的时间戳。
          if (bestkey == NULL || thisval > bestval) {
            bestkey = thiskey;
            bestval = thisval;
          }
        }
      }


      
      else if (server.maxmemory_policy == REDIS_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;
          }
        }
      }


      ... ...
      // 到这里,要删除的最优key已经选出来了。现在进入删除阶段。
      // 不论哪种策略,只要选出了最优key,就会执行以下删除流程。


      
      if (bestkey) {
        long long delta;


        robj *keyobj = createStringObject(bestkey,sdslen(bestkey));
        propagateExpire(db,keyobj);
        
        // 删除该bestkey对应的key-value值。注意这里既要从dict中删除,还要从expires中删除。
        delta = (long long) zmalloc_used_memory();
        dbDelete(db,keyobj);
        delta -= (long long) zmalloc_used_memory();
        mem_freed += delta;
        server.stat_evictedkeys++;
        notifyKeyspaceEvent(REDIS_NOTIFY_EVICTED, "evicted",
          keyobj, db->id);
        decrRefCount(keyobj);
        keys_freed++;


        
        if (slaves) flushSlavesOutputBuffers();
      }
    }
    if (!keys_freed) {
      latencyEndMonitor(latency);
      latencyAddSampleIfNeeded("eviction-cycle",latency);
      return REDIS_ERR; 
    }
  }
  latencyEndMonitor(latency);
  latencyAddSampleIfNeeded("eviction-cycle",latency);
  return REDIS_OK;
}

以上这篇关于redis Key淘汰策略的实现方法就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持编程网。

您可能感兴趣的文档:

--结束END--

本文标题: 关于redis Key淘汰策略的实现方法

本文链接: https://lsjlt.com/news/11914.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • 关于redis Key淘汰策略的实现方法
    1 配置文件中的最大内存删除策略 在redis的配置文件中,可以设置redis内存使用的最大值,当redis使用内存达到最大值时(如何知道已达到最大值?),redis会根据配置文件中的策略选取要删除的key...
    99+
    2022-06-04
    策略 方法 redis
  • 关于Redis的内存淘汰策略详解
    目录一、什么是内存淘汰?二、Redis 内存上限三、Redis 内存淘汰策略四、内存淘汰的具体工作步骤五、LRU 算法及在 Redis 中的改进5.1 LRU 算法5.2 Redis 中的 LRU 算法六、LFU一、什么...
    99+
    2023-05-19
    Redis 策略 Redis 内存淘汰
  • redis淘汰策略会删除磁盘上的key吗
    否,redis淘汰策略不会删除磁盘上的key。该策略仅针对内存中的key,以腾出空间给新key,而磁盘上的持久化数据不受影响。 Redis淘汰策略是否会删除磁盘上的Key 否,Redi...
    99+
    2024-04-19
    redis 数据访问
  • Redis的过期策略和内存淘汰策略
    文章前言 提到内存管理,我们就需要考虑Redis的内存过期策略和内存淘汰机制。该文章便从这两方面入手,分享一些在Redis内存方面相关的基础知识。 文章中使用的示例版本为Redis5.0版本。 内存过期策略 内存过期策略主要的...
    99+
    2020-12-25
    Redis的过期策略和内存淘汰策略
  • redis key键过期删除策略及淘汰机制探究
    目录Redis过期删除删除策略淘汰机制redis过期删除 redis的键可以设置过期时间,但是并不是每个键一到过期时间就会立即删除,redis不可能给每个设置过期时间的key上添加一个定时器来监视是否过期,CPU根本承受...
    99+
    2023-11-17
    redis key键过期删除 redis key
  • Redis缓存的淘汰策略是什么
    这篇文章主要讲解了“Redis缓存的淘汰策略是什么”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Redis缓存的淘汰策略是什么”吧!Redis(Remote...
    99+
    2024-04-02
  • 彻底弄懂Redis的LRU淘汰策略
    目录Redis的淘汰策略LRU算法简介实现思想推导巧用LinkedHashMap手写LRU第一步:构建DoubleLinkedList对象第二步:构建节点第三步:初始化DoubleL...
    99+
    2024-04-02
  • Redis的缓存淘汰策略有哪些
    Redis的缓存淘汰策略主要有以下几种: LRU(Least Recently Used):最近最少使用。根据键最近被访问的时间...
    99+
    2024-04-02
  • Redis的数据淘汰策略有哪些
    Redis的数据淘汰策略有以下几种: LRU(Least Recently Used):最近最少使用。该策略会淘汰最近最少被访问...
    99+
    2024-04-09
    Redis
  • redis的内存淘汰策略有哪些
    redis 提供了多项内存淘汰策略,以控制在内存不足情况下数据的处理方式。这些策略包括:noeviction:禁用内存淘汰,确保数据不会丢失。volatile-lru:淘汰最久未使用的已...
    99+
    2024-04-19
    redis 数据丢失
  • redis数据淘汰策略指的是什么
    redis数据淘汰策略指的是什么?这个问题可能是我们日常学习或工作经常见到的。希望通过这个问题能让你收获颇深。下面是小编给大家带来的参考内容,让我们一起来看看吧!1、淘汰简介Redis官方给的警告,当内存不...
    99+
    2024-04-02
  • Redis中LRU淘汰策略的深入分析
    前言 Redis作为缓存使用时,一些场景下要考虑内存的空间消耗问题。Redis会删除过期键以释放空间,过期键的删除策略有两种: 惰性删除:每次从键空间中获取键时,都检查取得的键是否过期,如果过期的话,...
    99+
    2024-04-02
  • Redis缓存中的淘汰策略有哪些
    本篇内容主要讲解“Redis缓存中的淘汰策略有哪些”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Redis缓存中的淘汰策略有哪些”吧!我们知道Redis缓存使用...
    99+
    2024-04-02
  • Redis 的内存淘汰策略和过期删除策略的区别
    目录前言过期删除策略如何设置过期时间?如何判定 key 已过期了?过期删除策略有哪些?Redis 过期删除策略是什么?内存淘汰策略如何设置 Redis 最大运行内存?Redis 内存淘汰策略有哪些?LRU 算法和 LFU...
    99+
    2022-07-04
    Redis 内存淘汰策略 Redis 过期删除策略
  • Redis 的内存淘汰策略和过期删除策略的区别
    目录前言过期删除策略如何设置过期时间?如何判定 key 已过期了?过期删除策略有哪些?Redis 过期删除策略是什么?内存淘汰策略如何设置 Redis 最大运行内存?Redis 内存...
    99+
    2024-04-02
  • redis缓存中怎么实施数据淘汰策略
    这篇文章将为大家详细讲解有关redis缓存中怎么实施数据淘汰策略,文章内容质量较高,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。在 redis 中,允许用户设置最大使用内存大小通过配置re...
    99+
    2024-04-02
  • redis 的 maxmemory 配置以及 缓存淘汰策略
    1. maxmemory 相关介绍 maxmemory 的作用 设置 redis 可用内存的上限。 maxmemory 的配置 将 maxmemory 设置为零将导致没有内存限制。这是 64 位系统的默认行为,而32位系统使用 3G...
    99+
    2015-05-05
    redis maxmemory 配置以及 缓存淘汰策略
  • 浅谈redis的maxmemory设置以及淘汰策略
    redis的maxmemory参数用于控制redis可使用的最大内存容量。如果超过maxmemory的值,就会动用淘汰策略来处理expaire字典中的键。 关于redis的淘汰策略: Redis提供了下面几...
    99+
    2022-06-04
    浅谈 策略 redis
  • Redis中LRU淘汰策略是怎么工作的
    在Redis中,LRU(Least Recently Used,最近最少使用)淘汰策略是一种缓存淘汰算法,它根据键的最近使用时间来决...
    99+
    2024-05-07
    Redis
  • Redis 缓存淘汰策略和事务实现乐观锁详情
    目录缓存淘汰策略标题LRU原理标题Redis缓存淘汰策略设置最大缓存淘汰策略Redis事务Redis事务介绍MULTIEXECDISCARDWATCHRedis 不支持事务回滚(为什么呢)Redis乐观锁Redis乐观锁...
    99+
    2022-07-21
    Redis 缓存淘汰策略 Redis 事务实现乐观锁
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作