Redis 缓存淘汰策略

当 Redis 被当作缓存使用时,也支持 LRU 或者 LFU 算法。Redis 的 maxmemory 指令用于将可用内存限制在固定大小内。当缓存大小达到内存最大值时,Redis会根据所设置的淘汰策略选择不同的行为。

maxmemory 100mb

淘汰策略

(在Redis术语中,通常将具有相关超时的键称为可变键)

  1. noeviction: 在达到内存限制并且客户端尝试执行可能导致使用更多内存的命令时返回错误。
  2. allkeys-lru: 尝试先删除最近最少使用的(LRU)键。
  3. volatile-lru: 尝试先删除最近最少使用的(LRU)可变键。
  4. allkeys-random:随机删除键。
  5. volatile-random:随机删除可变键。
  6. volatile-ttl: 尝试先删除剩余存活时间较少的可变键。
  7. volatile-lfu: 尝试先删除最不经常使用的可变键。
  8. allkeys-lfu: 尝试先删除最不经常使用的键。

如果Redis不存在可变键,或者可变键在删除后也达到足够的内存空间给新数据加入,就会Redis就会采取 noeviction 的策略返回错误。

使用经验

  1. 如果有一部分的缓存相对于其他缓存有更高的访问频率,则推荐使用allkeys-lru。
  2. 如果所有缓存访问频率都差不多,则推荐使用allkeys-random。
  3. 如果希望能够在创建缓存对象时通过使用不同的TTL值向Redis提供有关哪些是到期的最佳候选者的提示,则推荐使用volatile-ttl。

淘汰步骤:

  1. 客户端发送命令,添加数据到 Redis。
  2. Redis 检查内存使用情况,如果超出限制,使用设置的策略进行处理。
  3. 继续处理新命令,以此类推。

因此,Redis 会越过了内存限制,然后通过删除键是内存使用在最大值内。

Redis LRU 算法

由于 LRU 算法通常需要双向链表来记录缓存的访问顺序,会消耗一定的内存。

Redis 使用的是近似的 LRU 算法,方法是对随机获取少量几个键,从这些键中选出访问时间最久远的键,优选删除这些键来为新数据腾出空间。

可以通过 maxmemory-samples 设置采样数量。

maxmemory-samples 5

如何实现

1 .Redis 每个缓存对象内存储一个 lru-clock,存储的是最近一次访问时间的unix time stamp的低24位。

2. Redis 守护线程每秒更新一次server-lru-clock,是当前时间的 unix time stamp的低24位。

3. 假设配置的是 allkeys-lru 策略,随机挑选 5 个元素分别计算最后一次访问时间 lru-clock 到当前 server-lru-clock 的间隔,删除其中最久未被访问的元素。如果内存还是超过限制,则继续重新挑选,删除,直到内存在限制范围内。

如何计算访问时间间隔的(精度为1秒为前提进行讨论)

二进制24位最大值为16777215,大约是194天。如果一个缓存对象最后一次被访问是在194天前,就会出现 server-lru-clock小于该缓存对象的 lru-clock。

所以计算访问时间间隔有两种情况:

1. server-lru-clock >=  lru-clock, elapsedTime = serverLruClock - lruClock

2. server-lru-clock <  lru-clock,elapsedTime = 16777215(24位的最大值) - lruClock + serverLruClock

*源码分析参考:https://www.cnblogs.com/xinghebuluo/p/redis.html

*对比通过链表和哈希表实现的 LRU 算法:https://blog.csdn.net/z510463173/article/details/113736766

Redis LFU 算法

从Redis 4.0开始,可以使用最少使用算法。

Redis LFU会跟踪物品的访问频率,很少使用的元素会被驱逐,而经常使用的元素则有较高的机会保留在内存中。

Redis中实现LFU算法的时候,有这个两个重要的可配置参数:

  • server.lfu_log_factor : 能够影响计数的量级范围;
  • server.lfu_decay_time: 控制LFU计数衰减的参数。

整体思路:

  1. 利用 LRU 中使用到的24位的变量,16位用来存储访问时间,8位用来存储访问次数。
  2. lfu_log_factor 用来限制在访问键时,增加访问次数的概率(不是每次对键的访问,都会增加访问次数,这样就能用一个较小的数表示一个较大的量级)。
  3. lfu_decay_time 用来设置键的访问次数的衰减速度,单位是分钟。

增加访问次数:

Redis 会给新数据一个初始化的访问次数 5, 以免新数据的访问次数还未累积,就被淘汰策略删除。

如果一个键访问次数小于 5(因为有衰减),在对该键访问之后,访问次数就直接加1。

如果一个键访问次数不小于 5,在对该键访问之后,redis 不是简单对键的访问次数加1,而是先生成一个0~1的随机数 r,然后计算 1 / ( counter * factor + 1) 和 r 的大小关系。

当 r < 1 / ( counter * factor + 1) 时,则对访问次数加1;

当 r >= 1 / ( counter * factor + 1) 时,不操作访问次数。

所以整体来看,当键的counter越大,则增加的速度会越来越慢。

访问次数的衰减:

redis首先会计算该缓存最后一次访问时间(低8位)到当前时间的间隔,再除以配置的lfu_decay_time,得到该键从最后一次访问经历 n 个周期的lfu_decay_time,将 counter 减去n从而完成衰减。

访问时间到当前时间的时间间隔计算方法和LRU类似,不同的只是位数,LRU 最大 16777215,LFU 最大 255。

当访问键时:

当我们访问一个键时,会进行访问次数衰减,再进行增加访问次数的逻辑。

缓存淘汰的执行:

和LRU 类似,挑选几个键,然后每个都进行衰减操作,并且挑选出最少访问次数的键,删除。

你可能感兴趣的:(redis)