Redis内存满了怎么办?如何淘汰数据
1、Redis可以存多少数据?
在配置文件redis.conf中,可以通过参数maxmemory来设定最大运行内存
- 在32位系统中,maxmemory默认为3G,因为32位的机器最大支持4GB
- 在64位系统中,maxmemory默认位0,表示没有内存大小限制
当Redis存储超过这个maxmemory配置值,则触发Redis内存淘汰
2、如何淘汰
第一种noeviciton(默认淘汰策略):
- 如果内存达到maxmemory,则写入操作会失败,但不会淘汰已有数据
第二种多种淘汰策略,主要支持lru,lfu,random,ttl
- lru:根据lru算法尝试回收最长时间未使用的
- lfu:根据lfu驱逐最不常用的键
- random:回收随机的键
- ttl:回收过期集合的键,并且优先回收存活时间较短的键
这四种淘汰策略,可以选择时volatile,也就是设置了过期时间的key,或者是allkeys,即全部的key,所以一共有8种淘汰方式,如下图:
选择哪种淘汰算法
淘汰算法根据业务需求决定,如果数据非常重要,不能丢失,那就选择不淘汰,这种情况会导致写入失败,有完善的告警机制配合人工介入
如果是缓存场景,业务方一般使用lru/lfu这种灵活的淘汰策略
3、内存淘汰算法LRU和LFU
3.1、LRU
LRU算法是什么
最近久未使用,即记录每个key的最近访问时间,维护一个访问时间的数据
传统lur算法的实现基于链表结构,链表中的元素按照操作数序从前往后排列,最新操作的键会被移动到表头,当需要内存淘汰时,只需要删除链表尾部的元素即可,因为链表尾部的元素就代表最久未被使用的元素
Redis用LRU算法会有什么问题
如果为所有数据维护一个顺序列表,实际就是做一个双向链表,但是如果Redis数据稍微多些,这个链表就是巨大的成本,对于Redis而言,内存是最宝贵的,所以Redis选择了近似LRU算法
Redis 是如何实现LRU算法的
实现方式是在Redis的对象结构中添加一个额外的字段,用于记录此数据的最后一次访问时间
当Redis进行内存淘汰的时候,会使用随机采样的方式来淘汰数据,它是随机取5个值,然后淘汰最久没有使用的那个
优点:
- 不用为所有的数据维护一个链表,节省空间占用
- 不用在每次数据访问时都移动链表项,提升了缓存的性能
缺点:
- 无法解决缓存污染问题:有些数据只被读取了一次,但一直留存在Redis缓存中,造成缓存污染
Redis4.0之后LFU解决了这个问题
3.2、LFU
什么时LFU算法?
最近不常用算法;即根据数据访问次数来淘汰数据的
LFU算法会记录每个数据的访问次数,当一个数据被再次访问时,就会增加数据的访问次数。
所以,优先清理的就是哪些使用次数最少的数据
Redis如何实现LFU算法?
LFU算法相比于LRU算法的实现,多记录数据的方位频次的信息
typedef struct redisObject {
...
// 24 bits,用于记录对象的访问信息
unsigned lru:24;
...
} robj;
Redis对象头中的lru字段,在lru算法下和lfu算法下使用方式并不相同
在LRU算法中,Redis对象头的24bits lru字段是用来记录key的访问时间戳
在LFU算法中,Redis对象头的24bits lru字段被分为两段存储
- 高16位 idt :用来记录key的访问时间戳
- 低 8位 logc:用来记录key的访问频次,值越小使用频率越低,越容易被淘汰,初始值为5
注意:logc并不是单纯的访问次数,而是访问频次(访问频率),因为logc会随着时间推移而衰减的
在每次 key 被访问时,会先对 logc 做一个衰减操作,衰减的值跟前后访问时间的差距有关系
- 如果上一次访问的时间与这一次访问的���间差距很大,那么衰减的值就越大,
- 反之,衰减的值就会小
对 logc 做完衰减操作后,就开始对 logc 进行增加操作,增加操作并不是单纯的 + 1,而是根据概率增加,如果 logc 越大的 key,它的 logc 就越难再增加。
所以,Redis 在访问 key 时,对于 logc 是这样变化的:
- 先按照上次访问距离当前的时长,来对 logc 进行衰减;
- 然后,再按照一定概率增加 logc 的值
4、总结
内存淘汰策略是解决内存过大的问题,当 Redis 的运行内存超过最大运行内存时,就会触发内存淘汰策略,Redis 4.0 之后共实现了 8 种内存淘汰策略,我也对这 8 种的策略进行分类,如下:
转载自:https://juejin.cn/post/7390549988793778176