视频1 视频21 视频41 视频61 视频文章1 视频文章21 视频文章41 视频文章61 推荐1 推荐3 推荐5 推荐7 推荐9 推荐11 推荐13 推荐15 推荐17 推荐19 推荐21 推荐23 推荐25 推荐27 推荐29 推荐31 推荐33 推荐35 推荐37 推荐39 推荐41 推荐43 推荐45 推荐47 推荐49 关键词1 关键词101 关键词201 关键词301 关键词401 关键词501 关键词601 关键词701 关键词801 关键词901 关键词1001 关键词1101 关键词1201 关键词1301 关键词1401 关键词1501 关键词1601 关键词1701 关键词1801 关键词1901 视频扩展1 视频扩展6 视频扩展11 视频扩展16 文章1 文章201 文章401 文章601 文章801 文章1001 资讯1 资讯501 资讯1001 资讯1501 标签1 标签501 标签1001 关键词1 关键词501 关键词1001 关键词1501 专题2001
redis中设置lru算法的方法
2020-11-09 06:53:26 责编:小采
文档


1、设置Redis使用LRU算法

LRU(Least Recently Used)最近最少使用算法是众多置换算法中的一种。
Redis中有一个maxmemory概念,主要是为了将使用的内存限定在一个固定的大小。Redis用到的LRU 算法,是一种近似的LRU算法。

(1)设置maxmemory

上面已经说过maxmemory是为了限定Redis最大内存使用量。有多种方法设定它的大小。其中一种方法是通过CONFIG SET设定,如下:

127.0.0.1:6379> CONFIG GET maxmemory
1) "maxmemory"
2) "0"
127.0.0.1:6379> CONFIG SET maxmemory 100MB
OK
127.0.0.1:6379> CONFIG GET maxmemory
1) "maxmemory"
2) "104857600"

另一种方法是修改配置文件redis.conf:

maxmemory 100mb

注意,在bit系统下,maxmemory设置为0表示不Redis内存使用,在32bit系统下,maxmemory隐式不能超过3GB。
当Redis内存使用达到指定的时,就需要选择一个置换的策略。

(2)置换策略

当Redis内存使用达到maxmemory时,需要选择设置好的maxmemory-policy进行对老数据的置换。
下面是可以选择的置换策略:

  • noeviction: 不进行置换,表示即使内存达到上限也不进行置换,所有能引起内存增加的命令都会返回error

  • allkeys-lru: 优先删除掉最近最不经常使用的key,用以保存新数据

  • volatile-lru: 只从设置失效(expire set)的key中选择最近最不经常使用的key进行删除,用以保存新数据

  • allkeys-random: 随机从all-keys中选择一些key进行删除,用以保存新数据

  • volatile-random: 只从设置失效(expire set)的key中,选择一些key进行删除,用以保存新数据

  • volatile-ttl: 只从设置失效(expire set)的key中,选出存活时间(TTL)最短的key进行删除,用以保存新数据

  • 需要注意的是:

    (1)设置maxmemory-policy的方法和设置maxmemory方法类似,通过redis.conf或是通过CONFIG SET动态修改。

    (2)如果没有匹配到可以删除的key,那么volatile-lru、volatile-random和volatile-ttl策略和noeviction替换策略一样——不对任何key进行置换。

    (3)选择合适的置换策略是很重要的,这主要取决于你的应用的访问模式,当然你也可以动态的修改置换策略,并通过用Redis命令——INFO去输出cache的命中率情况,进而可以对置换策略进行调优。

    一般来说,有这样一些常用的经验:

  • 在所有的key都是最近最经常使用,那么就需要选择allkeys-lru进行置换最近最不经常使用的key,如果你不确定使用哪种策略,那么推荐使用allkeys-lru。

  • 如果所有的key的访问概率都是差不多的,那么可以选用allkeys-random策略去置换数据。

  • 如果对数据有足够的了解,能够为key指定hint(通过expire/ttl指定),那么可以选择volatile-ttl进行置换。

  • volatile-lru 和 volatile-random经常在一个Redis实例既做cache又做持久化的情况下用到,然而,更好的选择使用两个Redis实例来解决这个问题。

    设置是失效时间expire会占用一些内存,而采用allkeys-lru就没有必要设置失效时间,进而更有效的利用内存。

    (3)置换策略是如何工作的

    理解置换策略的执行方式是非常重要的,比如:

    1. 客户端执行一条新命令,导致数据库需要增加数据(比如set key value)

    2. Redis会检查内存使用,如果内存使用超过maxmemory,就会按照置换策略删除一些key

    3. 新的命令执行成功

    我们持续的写数据会导致内存达到或超出上限maxmemory,但是置换策略会将内存使用降低到上限以下。

    如果一次需要使用很多的内存(比如一次写入一个很大的set),那么,Redis的内存使用可能超出最大内存一段时间。

    (4)近似LRU算法

    Redis中的LRU不是严格意义上的LRU算法实现,是一种近似的LRU实现,主要是为了节约内存占用以及提升性能。Redis有这样一个配置——maxmemory-samples,Redis的LRU是取出配置的数目的key,然后从中选择一个最近最不经常使用的key进行置换,默认的5,如下:

    maxmemory-samples 5

    可以通过调整样本数量来取得LRU置换算法的速度或是精确性方面的优势。

    Redis不采用真正的LRU实现的原因是为了节约内存使用。虽然不是真正的LRU实现,但是它们在应用上几乎是等价的。下图是Redis的近似LRU实现和理论LRU实现的对比:

    测试开始首先在Redis中导入一定数目的key,然后从第一个key依次访问到最后一个key,因此根据LRU算法第一个被访问的key应该最新被置换,之后再增加50%数目的key,导致50%的老的key被替换出去。

    在上图中你可以看到三种类型的点,组成三种不同的区域:

    1. 淡灰色的是被置换出去的key

    2. 灰色的是没有被置换出去的key

    3. 绿色的是新增加的key

    理论LRU实现就像我们期待的那样,最旧的50%数目的key被置换出去,Redis的LRU将一定比例的旧key置换出去。

    可以看到在样本数为5的情况下,Redis3.0要比Redis2.8做的好很多,Redis2.8中有很多应该被置换出去的数据没有置换出去。在样本数为10的情况下,Redis3.0很接近真正的LRU实现。

    LRU是一个预测未来我们会访问哪些数据的模型,如果我们访问数据的形式接近我们预想——幂律,那么近似LRU算法实现将能处理的很好。

    在模拟测试中我们可以发现,在幂律访问模式下,理论LRU和Redis近似LRU的差距很小或者就不存在差距。

    如果你将maxmemory-samples设置为10,那么Redis将会增加额外的CPU开销以保证接近真正的LRU性能,可以通过检查命中率来查看有什么不同。

    通过CONFIG SET maxmemory-samples <count>动态调整样本数大小,做一些测试验证你的猜想。

    2、LRU的实现

    <?php
    /**
     * LRU是最近最少使用页面置换算法(Least Recently Used),也就是首先淘汰最长时间未被使用的页面
     */
    class LRU_Cache
    {
    
     private $array_lru = array();
     private $max_size = 0;
    
     function __construct($size)
     {
     // 缓存最大存储
     $this->max_size = $size;
     }
    
     public function set_value($key, $value)
     {
     // 如果存在,则向队尾移动,先删除,后追加
     // array_key_exists() 函数检查某个数组中是否存在指定的键名,如果键名存在则返回true,如果键名不存在则返回false。
     if (array_key_exists($key, $this->array_lru)) {
     // unset() 销毁指定的变量。
     unset($this->array_lru[$key]);
     }
     // 长度检查,超长则删除首元素
     if (count($this->array_lru) > $this->max_size) {
     // array_shift() 函数删除数组中第一个元素,并返回被删除元素的值。
     array_shift($this->array_lru);
     }
     // 队尾追加元素
     $this->array_lru[$key] = $value;
     }
    
     public function get_value($key)
     {
     $ret_value = false;
    
     if (array_key_exists($key, $this->array_lru)) {
     $ret_value = $this->array_lru[$key];
     // 移动到队尾
     unset($this->array_lru[$key]);
     $this->array_lru[$key] = $ret_value;
     }
    
     return $ret_value;
     }
    
     public function vardump_cache()
     {
     echo "<br>";
     var_dump($this->array_lru);
     }
    }
    
    $cache = new LRU_Cache(5); // 指定了最大空间 6
    $cache->set_value("01", "01");
    $cache->set_value("02", "02");
    $cache->set_value("03", "03");
    $cache->set_value("04", "04");
    $cache->set_value("05", "05");
    $cache->vardump_cache();
    echo "<br>";
    
    $cache->set_value("06", "06");
    $cache->vardump_cache();
    echo "<br>";
    
    $cache->set_value("03", "03");
    $cache->vardump_cache();
    echo "<br>";
    
    $cache->set_value("07", "07");
    $cache->vardump_cache();
    echo "<br>";
    
    $cache->set_value("01", "01");
    $cache->vardump_cache();
    echo "<br>";
    
    $cache->get_value("04");
    $cache->vardump_cache();
    echo "<br>";
    
    $cache->get_value("05");
    $cache->vardump_cache();
    echo "<br>";
    
    $cache->get_value("10");
    $cache->vardump_cache();
    echo "<br>";

    更多redis知识请关注redis入门教程栏目。

    下载本文
    显示全文
    专题