• 【Redis 设置Redis使用LRU算法】


    转自:http://ifeve.com/redis-lru/

    本文将介绍Redis在生产环境中使用的Redis的LRU策略,以及自己动手实现的LRU算法(php)

    1、设置Redis使用LRU算法

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

    (1)设置maxmemory

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

    1 127.0.0.1:6379> CONFIG GET maxmemory
    2 1) "maxmemory"
    3 2) "0"
    4 127.0.0.1:6379> CONFIG SET maxmemory 100MB
    5 OK
    6 127.0.0.1:6379> CONFIG GET maxmemory
    7 1) "maxmemory"
    8 2) "104857600"

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

    1 maxmemory 100mb

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

    (2)置换策略

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

    1 noeviction: 不进行置换,表示即使内存达到上限也不进行置换,所有能引起内存增加的命令都会返回error
    2 allkeys-lru: 优先删除掉最近最不经常使用的key,用以保存新数据
    3 volatile-lru: 只从设置失效(expire set)的key中选择最近最不经常使用的key进行删除,用以保存新数据
    4 allkeys-random: 随机从all-keys中选择一些key进行删除,用以保存新数据
    5 volatile-random: 只从设置失效(expire set)的key中,选择一些key进行删除,用以保存新数据
    6 volatile-ttl: 只从设置失效(expire set)的key中,选出存活时间(TTL)最短的key进行删除,用以保存新数据

    需要注意的是:

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

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

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

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

    1 在所有的key都是最近最经常使用,那么就需要选择allkeys-lru进行置换最近最不经常使用的key,如果你不确定使用哪种策略,那么推荐使用allkeys-lru
    2 如果所有的key的访问概率都是差不多的,那么可以选用allkeys-random策略去置换数据
    3 如果对数据有足够的了解,能够为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-samplesRedis的LRU是取出配置的数目的key,然后从中选择一个最近最不经常使用的key进行置换,默认的5,如下:

    1 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的实现

     1 <?php
     2 /**
     3  * LRU是最近最少使用页面置换算法(Least Recently Used),也就是首先淘汰最长时间未被使用的页面
     4  */
     5 class LRU_Cache
     6 {
     7 
     8     private $array_lru = array();
     9     private $max_size = 0;
    10 
    11     function __construct($size)
    12     {
    13         // 缓存最大存储
    14         $this->max_size = $size;
    15     }
    16 
    17     public function set_value($key, $value)
    18     {
    19         // 如果存在,则向队尾移动,先删除,后追加
    20         // array_key_exists() 函数检查某个数组中是否存在指定的键名,如果键名存在则返回true,如果键名不存在则返回false。
    21         if (array_key_exists($key, $this->array_lru)) {
    22             // unset() 销毁指定的变量。
    23             unset($this->array_lru[$key]);
    24         }
    25         // 长度检查,超长则删除首元素
    26         if (count($this->array_lru) > $this->max_size) {
    27             // array_shift() 函数删除数组中第一个元素,并返回被删除元素的值。
    28             array_shift($this->array_lru);
    29         }
    30         // 队尾追加元素
    31         $this->array_lru[$key] = $value;
    32     }
    33 
    34     public function get_value($key)
    35     {
    36         $ret_value = false;
    37 
    38         if (array_key_exists($key, $this->array_lru)) {
    39             $ret_value = $this->array_lru[$key];
    40             // 移动到队尾
    41             unset($this->array_lru[$key]);
    42             $this->array_lru[$key] = $ret_value;
    43         }
    44         
    45         return $ret_value;
    46     }
    47 
    48     public function vardump_cache()
    49     {
    50         echo "<br>";
    51         var_dump($this->array_lru);
    52     }
    53 }
    54 
    55 $cache = new LRU_Cache(5);                          // 指定了最大空间 6
    56 $cache->set_value("01", "01");
    57 $cache->set_value("02", "02");
    58 $cache->set_value("03", "03");
    59 $cache->set_value("04", "04");
    60 $cache->set_value("05", "05");
    61 $cache->vardump_cache();
    62 echo "<br>";
    63 
    64 $cache->set_value("06", "06");
    65 $cache->vardump_cache();
    66 echo "<br>";
    67 
    68 $cache->set_value("03", "03");
    69 $cache->vardump_cache();
    70 echo "<br>";
    71 
    72 $cache->set_value("07", "07");
    73 $cache->vardump_cache();
    74 echo "<br>";
    75 
    76 $cache->set_value("01", "01");
    77 $cache->vardump_cache();
    78 echo "<br>";
    79 
    80 $cache->get_value("04");
    81 $cache->vardump_cache();
    82 echo "<br>";
    83 
    84 $cache->get_value("05");
    85 $cache->vardump_cache();
    86 echo "<br>";
    87 
    88 $cache->get_value("10");
    89 $cache->vardump_cache();
    90 echo "<br>";
  • 相关阅读:
    centos6.5下的mysql5.6.30安装
    mysql常见错误
    fpm打包redis3.0.7
    centos6.5安装fpm打包工具
    centos6.5安装flume
    centos6.5 安装mono
    利用rsyslog 对linux 操作进行审计
    CentOS7修改服务器主机名方法
    CentOS 7 修改网卡名称为eth
    keepalived配置日志
  • 原文地址:https://www.cnblogs.com/xuelisheng/p/10857830.html
Copyright © 2020-2023  润新知