Redis:Redis键值淘汰策略

文章目录

  • 键值淘汰策略
    • [使用 maxmemory 配置指令](#使用 maxmemory 配置指令)
    • [为复制或持久化实例设置 maxmemory](#为复制或持久化实例设置 maxmemory)
    • 淘汰策略
    • [使用 INFO 命令](#使用 INFO 命令)
    • [近似 LRU 算法](#近似 LRU 算法)
    • [LFU 淘汰](#LFU 淘汰)

键值淘汰策略

Redis 常被用作缓存,以加速对较慢服务器或数据库的读取访问。由于缓存条目是持久存储数据的副本,当缓存内存不足时,淘汰它们通常是安全的(必要时将来可以再次缓存)。

Redis 允许你指定一个淘汰策略,当缓存大小超过设定的内存限制时自动淘汰键。每当客户端运行一个向缓存添加更多数据的新命令时,Redis 会检查内存使用情况。如果超过限制,Redis 会根据选择的淘汰策略淘汰键,直到总内存使用量回到限制以下。

注意,当一个命令向缓存添加大量数据(例如,将一个大集合的交集存储到一个新键中)时,内存可能会暂时大幅度超过限制。

下面的部分解释了如何为缓存配置内存限制,也描述了可用的淘汰策略以及何时使用它们。

使用 maxmemory 配置指令

maxmemory 配置指令指定用于缓存数据的最大内存量。你可以在启动时通过 redis.conf 文件设置 maxmemory。例如,要配置 100 兆字节的内存限制,你可以在 redis.conf 中使用以下指令:

text 复制代码
maxmemory 100mb

你也可以使用 CONFIG SET 在运行时通过 redis-cli 设置 maxmemory

text 复制代码
> CONFIG SET maxmemory 100mb

maxmemory 设置为零表示你不想限制数据集的内存。这是 64 位系统的默认行为,而 32 位系统使用 3GB 的隐式内存限制。 当缓存大小超过 maxmemory 设置的限制时,Redis 将强制执行你选择的淘汰策略,以防止缓存进一步增长。




客户端发送写入命令
内存是否超限?
正常写入数据
执行淘汰策略
移除一个或多个键
现在内存是否超限?

为复制或持久化实例设置 maxmemory

如果你正在为服务器使用复制或持久化,Redis 会使用一部分 RAM 作为缓冲区,用于存储等待写入副本或 AOF 文件的更新集。此缓冲区使用的内存不包括在用于判断是否需要淘汰的总内存中。

这是因为淘汰键本身也会生成必须添加到缓冲区的更新。如果更新被计入已用内存,那么在某些情况下,通过淘汰键节省的内存会立即被添加到缓冲区的更新数据消耗掉。这反过来会触发更多的淘汰,产生的反馈循环可能会不必要地从缓存中淘汰许多项目。

如果你正在使用复制或持久化,我们建议你设置 maxmemory 以留出一点空闲 RAM 来存储缓冲区。注意,对于 noeviction 策略,这并非必要(有关淘汰策略的更多信息,请参阅下面的部分)。

INFO 命令在内存部分返回一个 mem_not_counted_for_evict 值(你可以使用 INFO memory 选项仅查看此部分)。这是缓冲区当前使用的内存量。虽然确切数量会有所不同,但你可以使用它来估计在设置 maxmemory 之前要从总可用 RAM 中减去多少。

下面的示意图展示了在使用复制或 AOF 时,Redis 内存的划分情况,其中"复制缓冲区"不计入 maxmemory 限制:
70% 20% 10% Redis 实例内存分布 (启用复制/AOF) maxmemory 限制 (数据集) 复制缓冲区 (不计入 maxmemory) 其他开销

淘汰策略

使用 maxmemory-policy 配置指令来选择当达到 maxmemory 设置的限制时你想要使用的淘汰策略。 以下策略可用:

  • noeviction:不淘汰键,但当你尝试执行缓存新数据的命令时,服务器将返回错误。如果你的数据库使用复制,则此条件仅适用于主数据库。注意,仅读取现有数据的命令仍然正常工作。
  • allkeys-lru:淘汰最近最少使用 (LRU) 的键。
  • allkeys-lfu:淘汰最不经常使用 (LFU) 的键。
  • allkeys-random:随机淘汰键。
  • volatile-lru:淘汰设置了过期字段为 true 的键中最近最少使用的键。
  • volatile-lfu:淘汰设置了过期字段为 true 的键中最不经常使用的键。
  • volatile-random:仅在键设置了过期字段为 true 时随机淘汰键。
  • volatile-ttl:淘汰设置了过期字段为 true 且剩余生存时间 (TTL) 值最短的键。

你应该选择一种适合你的应用程序访问键的方式的淘汰策略:

  • 当你期望有一部分元素的访问频率远高于其他元素时,使用 allkeys-lru
    根据帕累托原则 ,这是一个非常常见的情况,所以如果你没有理由更喜欢其他策略,allkeys-lru 是一个很好的默认选项。
  • 当你期望所有键的访问频率大致相等时,使用 allkeys-random
    例如,当你的应用程序以重复循环的方式读取数据项时。
  • 如果你的代码可以估算哪些键是淘汰的良好候选者并给它们分配较短的 TTL,请使用 volatile-ttl
    另请注意,如果你充分利用了键过期,那么你不太可能遇到缓存内存限制,因为键通常会在需要淘汰之前就过期了。
  • volatile-lruvolatile-random 策略主要在你想使用单个 Redis 实例进行缓存和存储一组持久键时有用。
    但是,如果可能的话,你应该考虑在这种情况下运行两个独立的 Redis 实例。

另外请注意,为键设置过期值会消耗内存,所以像 allkeys-lru 这样的策略更节省内存,因为它不需要过期值就能运行。
是 (纯缓存)
否 (需保留部分键)
是, 符合二八定律
否, 分布均匀
关注访问频率


未设置 TTL
最近最少使用
最不经常使用
开始选择策略
是否所有键都可以被淘汰?
访问模式是否有热点?
淘汰的键是否有 TTL?
allkeys-lru
allkeys-random
allkeys-lfu
volatile-ttl
noeviction
关注时间还是最近访问?
volatile-lru
volatile-lfu

使用 INFO 命令

INFO 命令提供了一些有用的数据来检查缓存的性能。特别是,INFO stats 部分包括两个重要的条目,keyspace_hits(在缓存中成功找到键的次数)和 keyspace_misses(请求了一个键但不在缓存中的次数)。下面的计算给出了从缓存满足的尝试访问的百分比:

text 复制代码
keyspace_hits / (keyspace_hits + keyspace_misses) * 100

检查这是否大致符合你对你应用程序的预期(自然,百分比越高表示缓存性能越好)。

注意:

EXISTS 命令报告键不存在时,这被计为键空间未命中。

如果命中率低于预期,这可能意味着你没有使用最佳的淘汰策略。例如,如果你相信一小部分"热"数据(可以轻松放入缓存)应该占大约 75% 的访问,你可以合理地预期键空间命中百分比在 75% 左右。如果实际百分比较低,请检查 evicted_keys 的值(也由 INFO stats 返回)。高比例的淘汰表明你选择的策略过于频繁地淘汰了错误的键(所以 allkeys-lru 可能是一个不错的选择)。如果 evicted_keys 的值很低并且你正在使用键过期,请检查 expired_keys 以查看有多少键已过期。如果这个数字很高,你可能使用的 TTL 太低,或者你选择了错误的键进行过期,这导致键在应该保留之前就从缓存中消失了。
INFO 返回的其他有用信息包括:

  • used_memory_dataset :(内存部分)用于缓存数据的内存量。如果这大于 maxmemory,那么差值就是 maxmemory 被超出的量。
  • current_eviction_exceeded_time :(统计部分)自缓存上次开始超过 maxmemory 以来的时间。
  • commandstats 部分 :除其他事项外,这报告了向服务器发出的每个命令被拒绝的次数。如果你正在使用 noevictionvolatile_xxx 策略之一,你可以使用它来查找哪些命令被 maxmemory 限制阻止,以及这种情况发生的频率。

近似 LRU 算法

Redis LRU 算法使用最近最少使用的键的近似值,而不是精确计算它们。它随机抽取少量的键,然后淘汰自上次访问以来时间最长的键。

从 Redis 3.0 开始,该算法还跟踪一个良好的淘汰候选池。这提高了算法的性能,使其成为真正的 LRU 算法的接近近似值。

你可以通过 maxmemory-samples 配置指令更改每次淘汰前要检查的样本数来调整算法的性能:

text 复制代码
maxmemory-samples 5

Redis 不使用真正的 LRU 实现是因为它消耗更多的内存。然而,对于使用 Redis 的应用程序来说,该近似值实际上等同于真正的 LRU。该图比较了 Redis 使用的 LRU 近似值与真正的 LRU。 下面的示意图展示了 Redis LRU 算法如何通过随机采样来寻找淘汰目标,而不是遍历所有键:
Redis 内存
随机抽取
所有键集合
随机抽取 5 个样本
比较访问时间
找出最旧的键
淘汰

LFU 淘汰

从 Redis 4.0 开始,最不经常使用 淘汰模式可用。在某些情况下,此模式可能效果更好(提供更好的命中/未命中率)。在 LFU 模式下,Redis 将尝试跟踪项目的访问频率,因此很少使用的项目被淘汰。这意味着经常使用的键有更高的机会保留在内存中。

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

  • volatile-lfu:在设置了过期的键中使用近似 LFU 进行淘汰。
  • allkeys-lfu:使用近似 LFU 淘汰任何键。

LFU 像 LRU 一样被近似:它使用一个概率计数器,称为 Morris 计数器,仅使用每个对象的几位来估计对象访问频率,并结合一个衰减期,以便计数器随时间减少。在某个时刻,我们不再想将键视为经常访问的,即使它们在过去是这样,以便算法能够适应访问模式的变化。

该信息类似于 LRU(如本文档上一节所解释的)进行采样,以选择淘汰候选者。

然而与 LRU 不同,LFU 具有某些可调参数:例如,如果频繁的项目不再被访问,其排名降低的速度应该有多快?还可以调整 Morris 计数器范围以更好地将算法适应特定用例。

默认情况下,Redis 配置为:

  • 计数器在大约 100 万次请求时饱和。
  • 每一分钟衰减一次计数器。

这些应该是合理的值,并且已经过实验测试,但用户可能想要尝试这些配置设置以选择最佳值。

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

text 复制代码
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 计数器的衰减机制:随着时间推移,如果不被访问,计数器会逐渐减少,从而允许新的"热"数据进入。
T1: 访问 计数器 +10
T2: 访问 计数器 +10
等待... 衰减期
T3: 访问 计数器 +10
T4: 未访问 计数器 -1

相关推荐
皙然2 小时前
SpringBoot 自动装配深度解析:从底层原理到自定义 starter 实战(含源码断点调试)
java·spring boot·spring
重生之绝世牛码2 小时前
Linux软件安装 —— Redis集群安装(三主三从)
大数据·linux·运维·数据库·redis·数据库开发·软件安装
派大鑫wink2 小时前
【Day39】Spring 核心注解:@Component、@Autowired、@Configuration 等
java·后端·spring
魔芋红茶3 小时前
Spring Security 学习笔记 1:快速开始
笔记·学习·spring
Mr_sun.3 小时前
Day05——权限认证-SpringSecurity认证授权
spring
结衣结衣.3 小时前
Redis的基本全局命令以及数据类型和内部编码
数据库·redis·bootstrap
爱丽_4 小时前
Spring 框架
java·后端·spring
小北方城市网4 小时前
SpringBoot 集成 RabbitMQ 实战(消息队列):实现异步通信与系统解耦
java·spring boot·后端·spring·rabbitmq·mybatis·java-rabbitmq
雨中飘荡的记忆4 小时前
Spring Test 从入门到实战
java·后端·spring