原文地址: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里有更详细的说明。

近似LFU算法

  1. 取一个0到1之间的随机值R
  2. 计算P=1/(old_value*lfu_log_factor+1),这里的old_value就是上面说的counter值。
  3. 当R<P的时候counter加一。

不是学数学的不会证明这个算法,但是看起来这个算法本身不难,也是用的概率的方法,访问量和counter值之间大概是指数关系,用的时候参考上面那个表就行了。

用redis当作LRU缓存的更多相关文章

  1. Redis学习笔记2-使用 Redis 作为 LRU 缓存

    当 Redis 作为缓存使用时,当你添加新的数据时,有时候很方便使 Redis 自动回收老的数据.LRU 实际上是被唯一支持的数据移除方法.Redis 的 maxmemory 指令,用于限制内存使用到 ...

  2. Redis - 作为 LRU 缓存

    一.简介 LRU 实际上是被唯一支持的数据移除方法,同时也是 memcached 默认支持的缓存算法. 二.配置内存大小 在 redis.conf 文件中使用 maxmemory 指令能够配置内存大小 ...

  3. Redis作为lru缓存作用

    当 Redis 作为缓存使用时,当你添加新的数据时,有时候很方便使 Redis 自动回收老的数据.LRU 实际上是被唯一支持的数据移除方法.Redis 的 maxmemory 指令,用于限制内存使用到 ...

  4. redis(7)LRU缓存

    一.LRU简介 LRU是Least Recently Used的缩写,即:最近最少使用. 它是内存管理中的一种页面置换算法,对于在内存中但是又不用的数据块,操作系统会根据哪些数据属于LRU而将其移除内 ...

  5. Redis做LRU缓存

    当Redis用作缓存时,通常可以让它在添加新数据时自动逐出旧数据. 这种行为在开发人员社区中非常有名,因为它是流行的memcached系统的默认行为. LRU实际上只是支持的驱逐方法之一. 本页介绍了 ...

  6. redis文档翻译_LRU缓存

    Using Redis as an LRU cache使用Redis作为LRU缓存 出处:http://blog.csdn.net/column/details/redisbanli.html Whe ...

  7. 动手实现 LRU 算法,以及 Caffeine 和 Redis 中的缓存淘汰策略

    我是风筝,公众号「古时的风筝」. 文章会收录在 JavaNewBee 中,更有 Java 后端知识图谱,从小白到大牛要走的路都在里面. 那天我在 LeetCode 上刷到一道 LRU 缓存机制的问题, ...

  8. 在我们使用Redis作为一个LRU缓存的时候,怎么做才能更高效

    当用Redis作为一个LRU存储时,有些时候是比较方便的,在你增添新的数据时会自动驱逐旧的数据.这种行为在开发者论坛是非常有名的,因为这是流行的memcached系统的默认行为. LRU实际上只是支持 ...

  9. Redis只作为缓存,不做持久化的配置

    #1.配置缓存内存限制和清理策略 #作为缓存服务器,如果不加以限制内存的话,就很有可能出现将整台服务器内存都耗光的情况,可以在redis的配置文件里面设置: #example: # 限定最多使用1.5 ...

随机推荐

  1. 机器学习,详解SVM软间隔与对偶问题

    今天是机器学习专题的第34篇文章,我们继续来聊聊SVM模型. 我们在上一篇文章当中推导了SVM模型在硬间隔的原理以及公式,最后我们消去了所有的变量,只剩下了\(\alpha\).在硬间隔模型当中,样本 ...

  2. Agumaster添加股票日交易爬虫画面

  3. 计算Pi

    程序: package pai; import java.math.BigDecimal; public class ComplexPi { BigDecimal result; public Com ...

  4. PostgressQL

    环境 ubuntu18.04 docker 中 pull postgres:10 images 连接 进入容器内部 docker exec -it [mypostgres] /bin/bash 查看p ...

  5. My97DatePicker时间控件使用方法

    引入css及js <asp:TextBox ID="tb_startTime" runat="server" CssClass="Wdate&q ...

  6. IDEA中Javaweb项目图片加载不出来解决方案

    针对IDEA中Javaweb项目中无法加载图片 一.项目的结构及问题介绍 项目结构如下: 在500.jsp页面中访问了img目录下的相关图片,比如背景图片,我在500.jsp中是这样写的路径  bac ...

  7. 乔悟空-CTF-i春秋-Web-SQL

    2020.09.05 是不是有些题已经不能做了--费了半天,到最后发现做不出来,和网上大神的方法一样也不行,最搞笑的有个站好像是别人运营中的,bug好像被修复了-- 做题 题目 题目地址 做题 尝试简 ...

  8. [LeetCode]1249. 移除无效的括号(字符串,栈)

    题目 给你一个由 '('.')' 和小写字母组成的字符串 s. 你需要从字符串中删除最少数目的 '(' 或者 ')' (可以删除任意位置的括号),使得剩下的「括号字符串」有效. 请返回任意一个合法字符 ...

  9. [LeetCode]Sql系列2

    题目 1205. 每月交易II Transactions 记录表 +----------------+---------+ | Column Name | Type | +-------------- ...

  10. Java 审计之SSRF篇

    Java 审计之SSRF篇 0x00 前言 本篇文章来记录一下Java SSRF的审计学习相关内容. 0x01 SSRF漏洞详解 原理: 服务端提供了从其他服务器应用获取数据的功能且没有对目标地址做过 ...