使用 Redis 作为 LRU 缓存

https://redis.io/topics/lru-cache

当 Redis 用作缓存时,通常可以很方便地让它在您添加新数据时自动淘汰旧数据。这种行为在开发者社区中是众所周知的,因为它是流行的 memcached 系统的默认行为。

LRU 实际上只是支持的淘汰方法之一。本页涵盖了 Redis maxmemory 指令的更一般主题,该指令用于将内存使用量限制在固定数量,还深入介绍了 Redis 使用的 LRU 算法,这实际上是精确 LRU 的近似值。

从 Redis 版本 4.0 开始,引入了新的 LFU(最不常用)淘汰策略。这在本文档的单独部分中进行了介绍。

Maxmemory 配置指令

maxmemory 配置指令用于配置 Redis 为数据集使用指定数量的内存。可以使用 redis.conf 文件设置配置指令,或者稍后在运行时使用 CONFIG SET 命令。

例如,为了配置 100 M字节的内存限制,可以在 redis.conf 文件中使用以下指令

maxmemory 100mb

将 maxmemory 设置为零会导致没有内存限制。这是 64 位系统的默认行为,而 32 位系统使用 3GB 的隐式内存限制。

当达到指定的内存量时,可以在不同的行为中进行选择,称为策略。 Redis 可以只返回可能导致使用更多内存的命令的错误,或者它可以淘汰一些旧数据以便在每次添加新数据时返回到指定的限制。

淘汰策略

当达到最大内存限制时,Redis 的淘汰策略是使用 maxmemory-policy 配置指令配置的。

可以使用以下策略:

  • noeviction(不淘汰):当达到内存限制并且客户端尝试执行可能导致所有引起申请内存的命令会报错误(大部分的写命令, 删除键 和更多异常)。
  • allkeys-lru:首先通过尝试删除最近较少使用的 (LRU) key来淘汰key,以便为添加的新数据腾出空间。
  • volatile-lru:首先通过尝试删除最近较少使用的(LRU)key来淘汰key,但仅在设置了过期时间的key中,以便为添加的新数据腾出空间。
  • allkeys-random:随机淘汰key
  • volatile-random:随机淘汰key,但仅在设置了过期时间的key中
  • volatile-ttl:对于设置了过期时间的key中,首先尝试淘汰设置的TTL时间较短的key

如果没有匹配先决条件的淘汰键,则策略 volatile-lru、volatile-random 和 volatile-ttl 的行为类似于 noeviction。

根据应用程序的访问模式选择正确的淘汰策略很重要,但是您可以在应用程序运行时在运行时重新配置策略,并使用 Redis INFO 输出监控缓存未命中和命中的数量以调整您的设置.

一般来说,根据经验:

  • 当您期望请求的流行度呈幂律分布时,请使用 allkeys-lru 策略,也就是说,您期望元素子集的访问频率远高于其他元素。如果您不确定,这是一个不错的选择。

  • 如果您有一个循环访问,其中所有键都被连续扫描,或者您希望分布均匀(所有元素可能以相同的概率访问),请使用 allkeys-random。

  • 如果您希望能够通过在创建缓存对象时使用不同的 TTL 值向 Redis 提供关于哪些是好的过期候选者的提示,请使用 volatile-ttl。

  • volatile-lru 和 volatile-random 策略主要在您想使用单个实例进行缓存并拥有一组持久键时非常有用。然而,运行两个 Redis 实例来解决这样的问题通常是一个更好的主意。

还值得注意的是,为key设置过期时间会消耗内存,因此使用 allkeys-lru 之类的策略会提高内存效率,因为无需为在内存压力下被淘汰的密钥设置过期时间。

淘汰key的过程

重要的是要了解淘汰过程的工作方式如下:

  • 客户端运行新命令,导致添加更多数据
  • Redis 检查内存使用情况,如果大于 maxmemory 限制,它会根据策略淘汰key。
  • 执行新的命令,依次类推

所以我们不断地会超过内存限制,然后通过淘汰key保证内存始终在限制之下。

如果某个命令导致在一段时间内使用大量内存(例如有big key的集合),则内存限制可能会被明显超过。

近似 LRU 算法

Redis LRU 算法不是一个精确的实现。这意味着 Redis 无法选择淘汰key的最佳候选key,即过去访问次数最多的访问。相反,它将尝试运行 LRU 算法的近似值,通过对少量key进行采样,并淘汰采样key中最好的(具有最久访问时间)的key。

然而,自 Redis 3.0 以来,该算法得到了改进,也可以将一组好的候选对象用于淘汰。这提高了算法的性能,使其能够更接近真实 LRU 算法的行为。

Redis LRU 算法的重要之处在于,您可以通过更改样本数量来调整算法的精度来进行每次的淘汰。此参数由以下配置指令控制:

maxmemory-samples 5

Redis 之所以不使用真正的 LRU 实现,是因为它需要更多的内存。然而,对于使用 Redis 的应用程序来说,这个近似值几乎是等价的。以下是 Redis 使用的 LRU 近似与真实 LRU 的对比图。

生成上述图表的测试用给定数量的key填充了 Redis 服务器。key从第一个到最后一个被访问,因此第一个key是使用 LRU 算法淘汰的最佳候选者。后来增加了更多 50% 的密钥,以强制驱逐一半的旧密钥。

您可以在图中看到三种点,形成三个不同的带。

  • 浅灰色带是被淘汰的对象
  • 灰色带是未被淘汰的对象
  • 绿色带是添加的对象。

在理论上的 LRU 实现中,我们预计在之前的key中,前半部分将过期。 Redis LRU 算法只会以概率方式使以前的key过期。

与 Redis 2.8 相比,Redis 3.0 的 5 个样本做得更好,但大多数最新访问的对象仍由 Redis 2.8 保留。在 Redis 3.0 中使用 10 的样本大小,该近似值非常接近 Redis 3.0 的理论性能。

请注意,LRU 只是一个模型,用于预测给定key在未来被访问的可能性。此外,如果您的数据访问模式与幂律非常相似,那么大多数访问将在 LRU 近似算法能够很好地处理的key集中。

在模拟中,我们发现使用幂律访问模式,真正的 LRU 和 Redis 近似之间的差异很小或不存在。

但是,您可以以一些额外的 CPU 使用为代价将样本大小提高到 10,以接近真实的 LRU,并检查这是否会影响您的缓存未命中率。

使用如下命令在生产环境中试验不同的样本大小值非常简单。

 CONFIG SET maxmemory-samples <count>

比如:

config set maxmemory-samples 10

新的LFU模式

从 Redis 4.0 开始,提供了一种新的最不常用的淘汰模式。这种模式在某些情况下可能会更好(提供更好的命中/未命中率),因为使用 LFU Redis 会尝试跟踪项目的访问频率,因此很少使用的会被淘汰,而经常使用的有更高的概率会留在内存中。

如果您认为在 LRU,最近访问但实际上几乎从未请求过的key不会过期,因此风险是淘汰未来更有可能被请求的key。 LFU没有这个问题,一般来说应该更好地适应不同的访问模式。

要配置 LFU 模式,可以使用以下策略:

  • volatile-lfu 在具有过期的key中使用近似 LFU 算法去淘汰key。
  • allkeys-lfu 使用近似的 LFU 淘汰所有key。

LFU 类似于 LRU:它使用概率计数器,称为 Morris 计数器,以便使用每个对象的几个bit 来估计对象访问频率,并结合衰减周期,以便计数器随着时间的推移而减少:在某些时候,我们不再希望将key视为频繁访问,即使它们是以前的,以便算法可以适应访问模式的转变。

这些信息的采样与 LRU 发生的情况类似(如本文档上一节所述),以便选择淘汰的key。

然而,与 LRU 不同的是,LFU 具有某些可调参数:例如,如果不再访问排名较低的频繁项,它应该怎么处理更快呢?也可以调整Morris 计数器范围,以便更好地使算法适应特定的用例。

默认情况下,Redis 4.0 配置为:

  • 在大约 100 万个请求时使计数器饱和。
  • 每隔一分钟计数器减1。

这些应该是合理的值,并且经过了实验性测试,但用户可能希望使用这些配置设置来选择最佳值。

有关如何调整这些参数的说明可以在源代码分发中的示例 redis.conf 文件中找到,但简而言之,它们是:

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 文件注释中获得。

使用 Redis 作为 LRU 缓存相关推荐

  1. Redis的LRU缓存淘汰算法实现

    1 标准LRU的实现原理 LRU,最近最少使用(Least Recently Used,LRU),经典缓存算法. LRU会使用一个链表维护缓存中每个数据的访问情况,并根据数据的实时访问,调整数据在链表 ...

  2. LRU 缓存机制实现:哈希表 + 双向链表

    算法详解 LRU 缓存机制可以通过哈希表辅以双向链表实现,我们用一个哈希表和一个双向链表维护所有在缓存中的键值对. 双向链表按照被使用的顺序存储了这些键值对,靠近头部的键值对是最近使用的,而靠近尾部的 ...

  3. Redis的LRU算法

    2019独角兽企业重金招聘Python工程师标准>>> 整理自官方文档:将redis当做使用LRU算法的缓存来使用 当Redis被当做缓存来使用,当你新增数据时,让它自动地回收旧数据 ...

  4. Golang groupcache LRU 缓存简介与用法

    1.LRU LRU(Least Recently Used,最近最久未使用算法)是一种常见的缓存淘汰算法,当缓存满时,淘汰最近最久未使用的元素,在很多分布式缓存系统(如Redis, Memcached ...

  5. Redis学习、缓存、持久化、哨兵模式

    个人博客欢迎访问 总结不易,如果对你有帮助,请点赞关注支持一下 微信搜索程序dunk,关注公众号,获取博客源码 我写代码是为了更好的表达自我,这是艺术创作,而不单单是为了把事情搞定. -Antirez ...

  6. redis的lru原理_Redis的LRU机制介绍

    高并发架构系列:Redis的内存回收原理,及内存过期淘汰策略详解 Redis内存回收机制 Redis的内存回收主要围绕以下两个方面: 1.Redis过期策略删除过期时间的key值 **2.Redis淘 ...

  7. redis的lru原理_Redis的LRU算法

    Redis的LRU算法 LRU算法背后的的思想在计算机科学中无处不在,它与程序的"局部性原理"很相似.在生产环境中,虽然有Redis内存使用告警,但是了解一下Redis的缓存使用策 ...

  8. 缓存的有效期和淘汰策略【Redis和其他缓存】【刘新宇】

    缓存有效期与淘汰策略 有效期 TTL (Time to live) 设置有效期的作用: 节省空间 做到数据弱一致性,有效期失效后,可以保证数据的一致性 Redis的过期策略 过期策略通常有以下三种: ...

  9. oracle缓存和Redis,说说数据缓存那点事:Redis和memcached对比

    [此为"一森咖记"公众号--第86篇文章] 本文预计阅读15分钟 [引言] 当我们为一个并发量较大的应用做数据架构时,会考虑使用缓存,意欲达到三个目标: 1. 加快用户访问速度,提 ...

最新文章

  1. C# ManualResetEvent
  2. typeScript知识点总结
  3. 六年级计算机应用计划,2016年小学六年级信息技术教学计划 (800字)
  4. 父类与子类之间的关系
  5. 利用VMware Infrastructure SDK编程控制虚拟机集群(3)
  6. java 监控对象是什么_多线程-Java中的对象监视器是什么意思? 为什么要使用这个词?...
  7. 实验楼第三次实验报告
  8. oracle11g怎样进行闪回,模拟Oracle11g下用Flashback Data Archive进行恢复的若干场景
  9. 资金流学习 - 关注点
  10. 《21天学通Java(第6版)》—— 1.10 练习
  11. 3分钟全面了解元数据和数据元
  12. 微软云服务器的优点,探寻:微软私有云的优势究竟是什么
  13. recycleview添加item点击事件--作业三
  14. mixpanel umeng talkingdata
  15. 深入分析 synchronized 的实现原理
  16. Java六大线程池和四大拒绝策略
  17. 1725 天黑请闭眼
  18. IPU缩放图片的实现
  19. java显示图片缩略图_java中生成图片的缩略图
  20. 我喜欢Balsamiq Mockups的三大理由

热门文章

  1. 微信爬爬猫---公众号文章抓取代码分析
  2. 三星s9 android8.1.0,三星S9更新安卓9.0后Bixby运行异常
  3. android wifi 网桥,史上最全无线网桥知识,收藏这一篇就够了!
  4. 程序设计 C语言飞机大战
  5. 2021陕西安全员C证考试及(安全员)模拟考试答案解析
  6. 微信小程序-实现录制视频(附部分代码)
  7. 记录一次vxworks下使用NFS组件的过程
  8. 教你用最新工具反编译android apk
  9. 收集 Linux 相关专业术语发音
  10. SpringBoot调用第三方IP查询接口(Https)