当 Redis 被当作缓存使用时,也支持 LRU 或者 LFU 算法。Redis 的 maxmemory
指令用于将可用内存限制在固定大小内。当缓存大小达到内存最大值时,Redis会根据所设置的淘汰策略选择不同的行为。
maxmemory 100mb
(在Redis术语中,通常将具有相关超时的键称为可变键)
如果Redis不存在可变键,或者可变键在删除后也达到足够的内存空间给新数据加入,就会Redis就会采取 noeviction 的策略返回错误。
使用经验:
淘汰步骤:
因此,Redis 会越过了内存限制,然后通过删除键是内存使用在最大值内。
由于 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 4.0开始,可以使用最少使用算法。
Redis LFU会跟踪物品的访问频率,很少使用的元素会被驱逐,而经常使用的元素则有较高的机会保留在内存中。
Redis中实现LFU算法的时候,有这个两个重要的可配置参数:
整体思路:
增加访问次数:
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 类似,挑选几个键,然后每个都进行衰减操作,并且挑选出最少访问次数的键,删除。