用redis当作LRU缓存

原文地址:https://redis.io/topics/lru-cache

Redis可以用来作缓存,他可以很方便的淘汰(删除)旧数据添加新数据,类似memcached。LRU只是其中的一种置换算法,这篇文章介绍了maxmemory配置命令和LRU算法的一些深入讨论,这里的LRU只是一种近似LRU(并不是严格的把最老的数据淘汰,而是使用随机采样的方式)。从4.0开始Redis引入了一种新的淘汰算法LFU(Least Frequently Used)。

maxmemory配置命令

maxmemory配置命令用来指定Redis存储数据的大小,可以在redis.conf里配置,也可以运行的时候用CONFIG SET配置。比如配置成使用100M内存,配置命令如下

maxmemory 100mb

如果把maxmemory设置成0就是不对内存使用量做限制。在64位系统上默认是设置成0,在32位系统上默认设置成3GB。如果达到了限制内存,会根据配置采取不同的策略,可以给相应的命令返回错误,也可以淘汰旧数据来存入新数据。

淘汰策略

在配置文件里使用maxmemory-policy命令指定淘汰策略,取值如下

  • noeviction:达到限制内存以后再存新数据会返回出错。
  • allkeys-lru:淘汰最近没使用的数据。
  • volatile-lru:在设置了过期值(expire)的数据里淘汰最近没使用的数据。
  • allkeys-random:随机淘汰数据。
  • volatile-random:在设置了过期值(expire)的数据里随机淘汰数据。
  • volatile-ttl:在设置了过期值(expire)的数据里淘汰快要过期的数据。

volatile-lru,volatile-random和volatile-ttl在没有可淘汰的数据的时候也会像noeviction一样返回出错。怎样选择淘汰策略取决于你的程序,也可以在使用的过程中用INFO命令观察命中率再做调整。一般情况按如下方法选择淘汰策略:

  • 如果所存储的数据访问量成幂律分布的,也就是说一部分数据的访问量明显多于其他数据,那么就使用allkeys-lru。
  • 如果所存储的数据周期访问的,或者被访问的概率大致相同,那么就是用allkeys-random。
  • 如果所存储的数据设置了不同的过期时间,可以用volatile-ttl。

如果在一个redis实例里存储了两种数据(永久数据和带过期时间的数据),使用volatile-lru和volatile-random是比较好的选择。当然更好的办法是把这两种数据分别存在一个redis实例里。给数据设置过期时间是需要消耗内存的,所有使用allkeys-lru会更节省内存。

淘汰数据的过程

  • 客户端请求添加数据。
  • redis检查内存是否超过了maxmemory限制,如果超过了就根据策略淘汰旧数据。
  • 添加客户端要求的添加的数据。

所有内存使用量会一直在maxmemory上下徘徊,如果某个命令要求添加一个很大的数据很可能造成redis使用的内存明显超过了maxmemory限制。

近似LRU算法

redis的LRU没有严格的实现LRU,也就是说redis不是淘汰最佳(最久没访问)的数据。redis会做一个随机采样,淘汰样本里最佳数据。3.0以后redis会使用备选池做淘汰,提升了性能,准确性更高,更接近LRU算法(代码里提升性能很明显,准确率更高这点看不出来,可能官方做统计得出的结论)。可以设置样本大小调整算法的准确率。

maxmemory-samples 5

redis没用真正的LRU实现是因为这正的LRU太消耗内存了,要遍历排序,会慢很多。下图是近似LRU算法和真正的LRU算法的测试对比图

测试的时候先生成一些数据。从第一个数据开始存储,一直到最后一个,所以第一个数据是LRU算法的最佳淘汰对象。最后又添加50%的数据用来淘汰旧数据。上面的三个色带分别是:

  • 浅灰色了淘汰的数据
  • 灰色是没有淘汰的数据
  • 绿色是添加的数据。

理论上最先添加的一半数据应该淘汰,redis的LRU算法只是概率性的淘汰这些旧数据。可以看出来在maxmemory-samples设置成5的时候3.0版本比2.8版本更精确(相对来说更接近理论值)。maxmemory-samples设置成10的时候3.0版本已经相对接近理论值了。

LRU只是预测将来一段时间的数据访问模型,如果你的数据成幂律分布redis的LRU也能非常好的处理这个模型。 redis的LRU和真正的LRU差距非常小甚至没差距。如果想要更接近LRU可以提高样本采集量把maxmemory-samples设置成10。

新的近似LFU模式

从4.0开始redis引入了一个新的淘汰模型LFU(Least Frequently Used)。一些情况下这个模型在命中率上更会准确。LFU会统计数据的使用率,使用率低的会被淘汰,使用率高的留下。LRU会保留最近访问了但是平常访问率很低的数据,风险就是淘汰了一个平常访问率高但是最近没访问的数据。LFU不会存在这样的问题,所以LFU更适用于各种不同的访问模型。LFU策略配置如下:

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

LFU和LRU类似也是一种概率计算,LFU使用一个叫morris counter的概率统计方法,一个数据只使用几个比特。和一个叫过期时间(decay period)的数结合使用。统计值(counter)随着时间减小。LFU也是使用采样的方式淘汰数据。LFU有更多的调整选项,4.0默认的配置

  • 一百万访问量会使统计值(counter)变为最大
  • 衰败期是一分钟

这些是经过测试比较好用的,用户也可以自己设置。配置命令如下:

lfu-log-factor 10
lfu-decay-time 1

一个比较特殊的用法是衰败期设置成0,每次统计都会衰败,这是个比较少用的方法。指数因子用来指定多大的访问量会是统计值变为最大,统计值范围0-255。指数因子越大需要越多的访问量使统计值变为最大,指数因子越小访问量低的时候分辨率越高。

+--------+------------+------------+------------+------------+------------+
| factor | 100 hits   | 1000 hits  | 100K hits  | 1M hits    | 10M hits   |
+--------+------------+------------+------------+------------+------------+
| 0      | 104        | 255        | 255        | 255        | 255        |
+--------+------------+------------+------------+------------+------------+
| 1      | 18         | 49         | 255        | 255        | 255        |
+--------+------------+------------+------------+------------+------------+
| 10     | 10         | 18         | 142        | 255        | 255        |
+--------+------------+------------+------------+------------+------------+
| 100    | 8          | 11         | 49         | 143        | 255        |
+--------+------------+------------+------------+------------+------------+

所以指数因子需要在分辨率和高访问之间做一个折中。redis.conf里有更详细的说明。

猜你喜欢

转载自www.cnblogs.com/4a8a08f09d37b73795649038408b5f33/p/10146904.html