计算机内存不是无限大的,都有用完的时候,当内存满时,便触发Redis淘汰策略。
淘汰策略
淘汰策略总共8种。
不进行数据淘汰的策略
noeviction,默认使用的配置。
noeviction策略是缓存被写满了,再有写请求来时,Redis 不再提供服务,而是直接返回错误。
仅淘汰配置过期时间key
volatile-random、volatile-ttl、volatile-lru、volatile-lfu(Redis 4.0 后新增)。
volatile-ttl: 根据过期时间的先后进行删除,越早过期的越先被删除。
volatile-random:在设置了过期时间的键值对中,进行随机删除。
volatile-lru:会使用 LRU 算法(最近最少使用)筛选设置了过期时间的键值对。
volatile-lfu :会使用 LFU 算法(最不经常使用)选择设置了过期时间的键值对。
淘汰所有缓存类型的key
allkeys-lru、allkeys-random、allkeys-lfu(Redis 4.0 后新增)三种。
allkeys-random 策略,从所有键值对中随机选择并删除数据;
allkeys-lru 策略,使用 LRU 算法(最近最少使用)在所有数据中进行筛选。
allkeys-lfu 策略,使用 LFU 算法(最不经常使用)在所有数据中进行筛选。
lru和lfu对比
如果我们的服务中有冷热数据隔离需求,lru无疑是一个比较好的办法。可以将缓存的一些不经常使用的冷数据,而且数据size比较大的,筛选出来清理掉。而近期频繁被使用的key就被保留下来了。
常见的场景如下:
- 电商平台的冷热数据:某个爆款的商品、秒杀商品热点信息可以保留下来。
- 外卖平台:每天的11-13点,17-19点,一定是美食外卖品种的高频率访问时间段,而日用品、果蔬生鲜 大都会避开这个高峰期,这时如果内存不够用了,那么就会成为被优先删除的缓存类型。
LRU算法的不足之处在于,一个本身很少被访问的key,只是刚刚被访问了1次,就被认为是最近有使用的热点数据,导致短时间内不会被淘汰。
而LFU弥补了这个不足,LFU(Least Frequently Used)淘汰策略会根据key的最近访问频率进行淘汰,解决上面说的这个不足。
- LFU在LRU的基础上,为每个数据增加了一个计数器,用于统计该数据的访问次数。
- 当使用LFU策略淘汰数据时,会根据数据的访问次数进行筛选,把访问次数最低的数据淘汰出内存。
- 如果两个缓存数据的访问次数相同,LFU再比较这两个key最近一次的访问时间,把访问时间更早的缓存key淘汰出内存。
我们电商项目生产环境使用volatile-lru淘汰方式,因为Redis定义为只是缓存数据,如果Redis不存在数据通过其他数据源(MySQL、Es等)提供并写入Redis,使用volatile-lru可以减少淘汰key数据量(实际应用中可能不设置过期时间可以很少)、避免了lfu额外统计数据的开销。
什么时候触发淘汰策略?
在Redis Memory占用超过我们配置的阈值的时候触发策略执行。
设置最大内存:
# redis.conf 配置最大内存空间占用为2gb,超过则执行内存淘汰策略
redis > CONFIG SET maxmemory 2gb
查询最大内存:
> config get maxmemory
maxmemory
536870912
淘汰算法
LRU算法
LRU是Least Recently Used的缩写,按照最近最少使用的原则来筛选数据,最不常用的数据会被筛选出来,而最近频繁使用的数据会留在缓存中。
LRU算法需要一个双向链表来记录数据的最近被访问顺序,但是出于节省内存的考虑,Redis的LRU算法并非完整的实现。
Redis并不会选择最久未被访问的键进行回收,相反它会尝试运行一个近似LRU的算法,通过对少量键进行取样,然后回收其中的最久未被访问的键。通过调整每次回收时的采样数量maxmemory-samples,可以实现调整算法的精度。
Redis Object的lru属性(Redis核心技术-数据结构3-String、Redis Object),以秒为单位存储了对象新建或者更新时的unix time,也就是LRU clock。
LFU
LFU(least frequently used (LFU) page-replacement algorithm)。即最不经常使用页置换算法,要求在页置换时置换引用计数最小的页,因为经常使用的页应该有一个较大的引用次数。
LFU是在Redis4.0后出现的,LRU的最近最少使用实际上并不精确,考虑下面的情况,如果在|处删除,那么A距离的时间最久,但实际上A的使用频率要比B频繁,所以合理的淘汰策略应该是淘汰B。LFU就是为应对这种情况而生的。
A~~A~~A~~A~~A~~A~~A~~A~~A~~A~~~|
B~~~~~B~~~~~B~~~~~B~~~~~~~~~~~B|
LFU把原来的key对象的内部时钟的24位分成两部分,前16位还代表时钟,后8位代表一个计数器。16位的情况下如果还按照秒为单位就会导致不够用,所以一般这里以时钟为单位。而后8位表示当前key对象的访问频率,8位只能代表255,但是redis并没有采用线性上升的方式,而是通过一个复杂的公式,通过配置两个参数来调整数据的递增速度。
淘汰策略配置
redis.conf配置:
# 淘汰策略,默认:noeviction
# maxmemory-policy noeviction
# LRU、LFU 和最小 TTL 算法不是精确算法而是近似算法
# 算法(为了节省内存),所以你可以调整它的速度或
# 准确性。 默认情况下,Redis 将检查五个键并选择一个
# maxmemory-samples 5
总结
一张图总结
参考:
https://www.jianshu.com/p/c8aeb3eee6bc