Redis-策略和原理

前言

每天学习一点新东西,今天准备来看看Redis 内部的这些策略,也算是补了前段时间给自己挖的坑(坑在上一篇博客中),这个坑得慢慢的填啊。🙃

内容

  • 键过期删除策略
  • 内存淘汰策略

键过期删除策略

在Redis 中一共存在三种对于键过期的删除策略:

  • 定时删除: 在设置键的过期时间的同时,创建一个定时器timer ,让定时器在键的过期时间来临时,立即执行对键执行删除操作。
  • 惰性删除: 放任键过期不管,但是每次从键空间中获取键时,都检查取得的键是否过期,如果过期的话,就删除该键;如果没有过期,就返回该键。
  • 定期删除: 每隔一段时间,程序就对数据库进行一次检查,删除里面的过期键。至于要删除多少过期键,以及要检查多少个数据库, 则由算法决定。

在以上的三种键过期删除策略中,只有第一种和第三种是主动删除策略,第二种则是被动删除策略。

定时删除

定时删除策略对内存是最友好的,通过使用定时器,定时删除策略可以保证过期键会尽可能快地被删除,并释放过期键所占用的内存。
另一方面,定时删除策略的缺点是,它对CPU 时间是最不友好的,在过期键比较多的情况下,删除过期键这一行为可能会占用相当一部分CPU 时间,在内存不紧张但是CPU 时间非常紧张的情况下.将CPU 时间用在删除和当前任务无关的过期键上,无疑会对服务器的响应时间和吞吐量造成影响。

惰性删除

惰性删除策略对CPU 时间来说是最友好的,程序只会在取出键时才对键进行过期检查,这可以保证删除过期键的操作只会在非做不可的情况下进行, 并且删除的目标仅限于当前处理的键,这个策略不会在删除其他无关的过期键上花费任何CPU 时间。
惰性删除策略的缺点是,它对内存是最不友好的,如果一个键已经过期,而这个键又仍然保留在数据库中,那么只要这个过期键不被删除,它所占用的内存就不会释放。在使用惰性删除策略时,如果数据库中有非常多的过期键,而这些过期键又恰好没有被访问到的话,那么它们也许永远也不会被删除(除非用户手动执行FLUSHDB ),我们甚至可以将这种情况看作是一种内存泄漏一一无用的垃圾数据占用了大量的内存,而服务器却不会自己去释放它们,这对于运行状态非常依赖于内存的Redis 服务器来说,肯定不是一个好消息。

定期删除

从上面对定时删除和惰性删除的讨论来看,这两种删除方式在单一使用时都有明显的缺陷:
定时删除占用太多CPU 时间,影响服务器的响应时间和吞吐量。惰性删除浪费太多内存,有内存泄漏的危险。
定期删除策略是前两种策略的一种整合和折中。

定期删除策略每隔一段时间执行一次删除过期键操作,并通过限制删除操作执行的时长和频率来减少删除操作对CPU 时间的影响。除此之外,通过定期删除过期键,定期删除策略有效地减少了因为过期键而带来的内存浪费。 定期删除策略的难点是确定删除操作执行的时长和频率。
如果删除操作执行得太频繁,或者执行的时间太长,定期删除策略就会退化成定时删除策略,以至于将CPU 时间过多地消耗在删除过期键上面。
如果删除操作执行得太少,或者执行的时间太短,定期删除策略又会和惰性删除策略一样,出现浪费内存的情况。
因此,如果采用定期删除策略的话,服务器必须根据情况,合理地设置删除操作的执行时长和执行频率。

Redis 官方键过期删除策略

实际上Redis 服务器实际使用的是惰性删除和定期删除两种策略,通过配合使用这两种删除策略,服务器可以很好地在合理使用CPU 时间和避免浪费内存空间之间取得平衡。


内存淘汰策略

在将Redis 用作缓存时, 如果内存空间用满时, 就会自动驱逐老的数据。 默认情况下采用memcached 就是这种方式, 这个大部分开发者都比较熟悉。而LRU 算法就是Redis 唯一支持的回收算法。

修改Redis 占用内存

  1. 配置文件
    修改启动Redis 时传入的配置文件中的设置内存大小

    1
    2
    //设置Redis最大占用内存大小为100M
    maxmemory 100mb
  2. 命令
    Redis 支持动态的修改内存大小

    1
    2
    3
    4
    //设置Redis最大占用内存大小为100M
    127.0.0.1:6379> config set maxmemory 100mb
    //获取设置的Redis能使用的最大内存大小
    127.0.0.1:6379> config get maxmemory

内存淘汰

Redis 可以设置内存的大小,那么在内存使用完之后,该怎么办呢?所以Redis 定义了以下几种方式来处理这种情况:

  • noeviction(默认策略): 对于写请求不再提供服务,直接返回错误(DEL请求和部分特殊请求除外)。
  • allkeys-lru: 从所有key 中使用LRU 算法进行淘汰。
  • volatile-lru: 从设置了过期时间的key 中使用LRU 算法进行淘汰。
  • allkeys-random: 从所有key 中随机淘汰数据。
  • volatile-random: 从设置了过期时间的key 中随机淘汰。
  • volatile-ttl: 在设置了过期时间的key 中,根据key 的过期时间进行淘汰,越早过期的越优先被淘汰。

修改内存淘汰策略

获取当前内存淘汰策略

1
127.0.0.1:6379> config get maxmemory-policy

复制代码通过配置文件设置淘汰策略(修改redis.conf 文件):

1
maxmemory-policy allkeys-lru

复制代码通过命令修改淘汰策略:

1
127.0.0.1:6379> config set maxmemory-policy allkeys-lru

LRU 算法

LRU(Least Recently Used ),即最近最少使用,是一种缓存置换算法。在使用内存作为缓存的时候,缓存的大小一般是固定的。当缓存被占满,这个时候继续往缓存里面添加数据,就需要淘汰一部分老的数据,释放内存空间用来存储新的数据。这个时候就可以使用LRU 算法。其核心思想是:如果一个数据在最近一段时间没有被用到,那么将来被使用到的可能性也很小,所以就可以被淘汰掉。

Redis 中的LRU 算法(近似LRU 算法)

Redis 使用的是近似LRU 算法,它跟常规的LRU 算法还不太一样。近似LRU 算法通过随机采样法淘汰数据,每次随机出5(默认)个key ,从里面淘汰掉最近最少使用的key 。

LRU算法的对比:
YunfL8.jpg

可以看到图中有三种不同颜色的点:

  • 浅灰色是被淘汰的数据
  • 灰色是没有被淘汰掉的老数据
  • 绿色是新加入的数据
    我们能看到Redis3.0 采样数是10 生成的图最接近于严格的LRU 。而同样使用5 个采样数,Redis3.0 也要优于Redis2.8 。

LFU 算法

LFU 算法是Redis4.0 里面新加的一种淘汰策略。它的全称是Least Frequently Used ,它的核心思想是根据key 的最近被访问的频率进行淘汰,很少被访问的优先被淘汰,被访问的多的则被留下来。

LFU 算法能更好的表示一个key 被访问的热度。假如你使用的是LRU 算法,一个key很久没有被访问到,只刚刚是偶尔被访问了一次,那么它就被认为是热点数据,不会被淘汰,而有些key 将来是很有可能被访问到的则被淘汰了。如果使用LFU算法则不会出现这种情况,因为使用一次并不会使一个key 成为热点数据。
LFU 一共有两种策略:

  • volatile-lfu:在设置了过期时间的key中使用LFU算法淘汰key。
  • allkeys-lfu:在所有的key中使用LFU算法淘汰数据。

后记

引用

http://www.redis.cn/topics/lru-cache.html

总结

感觉以后还是需要多看看官方文档!


个人备注

此博客内容均为作者学习所做笔记,侵删!
若转作其他用途,请注明来源!