Redis内存淘汰

2024-08-24 07:04
文章标签 内存 redis 淘汰

本文主要是介绍Redis内存淘汰,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

Redis内存淘汰

Redis可以存储多少数据

maxmemory配置,默认是注释掉的。

#maxmemory <bytes>

我们可以主动配置maxmemory,maxmemory支持各种单位,默认是字节

maxmemory 1024
maxmemory 1024KB
maxmemory 1024MB
maxmemory 1024GB

当Redis存储超过这个值,就会触发内存淘汰。

淘汰策略

有两个方向,一个是noevicition,默认就是这种策略,如果内存达到maxmemory,则写入操作失败,但不会淘汰原有数据;另一个是多种淘汰策略,主要是LRU、LFU、RANDOM、TTL。

image-20240822215406391

LRU(Least recently used,最近最少使用):尝试回收最长时间未使用的。

LFU(Least Frequently Use,最近最少频率使用):尝试回收最不常用的键。

Random:随机回收键。

TTL:回收过期集合的键,并且优先回收存活时间较短(TTL)的键。

一般四种策略又分为volatile,设置了过期时间的Key;或是allkeys,全部的Key。

选择哪种淘汰算法

一般根据业务需求决定,缓存场景下一般使用的是LRU或LFU。

LRU

什么是LRU

最近最久未使用,即记录每个Key的最近访问时间,维护一个访问时间的数据

Redis的近似LRU

为什么不直接使用LRU

因为对所有数据用一个双向链表维护就是一个巨大的成本,Redis本身就是使用内存的,而内存是又少又珍贵的资源,所以Redis选择实现一个近似LRU。

近似的LRU

LRU模式中,RedisObject中lru字段存储的是被访问时的时间,每次该key被访问就会更新这个字段。

llru字段表示的时间戳越小,代表key空闲的时间越长,越应该被淘汰

为了保证高性能,Redis还缓存了Unix操作系统的时钟。

近似的LRU算法中,在内存不足时就会,进行全局随机采样,来筛选准备淘汰的元素。

步骤为:

1.随机采样n个key,n默认为5

2.根据时间戳淘汰最旧的key

3.判断内存是否不足,不足循环步骤1;反之,退出淘汰

之前说的全局随机采样,也是一个范围性的选择。也就是之前提过的,allkeys或者volatile

淘汰池的优化

近似LRU,最大的优点就是节约了内存。但是它的缺点是,淘汰的key是随机采样的结果,并不是真正的全局最久未访问。

在Redis3.0中,对近似LRU进行了优化:加入淘汰池。

优化后的LRU会维护一个大小为16的候选池,池中的数据按时间进行排序。

步骤:

1.第一次是随机挑选的16个,第一次以后就是随机挑选的一个

2.按访问时间进行排序,并且淘汰最久未访问的(一次只淘汰一个,剩下的都在池中)

3.判断是否继续

LRU优化后的对比

image-20240822222328915

浅灰色是被淘汰的数据

灰色是没有被淘汰的老数据

绿色是新加入的数据

我们几乎可以认为Redis的近似LRU已经达到正常LRU的水准了。

LFU

什么是LFU

最不频繁淘汰算法,优先淘汰活跃最低,使用频率最低的。

为什么引入LFU

Redis在4.0版本后引入了LFU算法,为什么?

因为在部分场景下,只根据最近访问,不谈频率得不到我们希望的结果。

比如,有两个键key1、key2,之前9次一直在访问key1,第10次访问了key2,第10次后触发了内存淘汰,那你觉得该淘汰key1还是key2?

如果我们使用LRU,那就是淘汰key1;如果我们使用LFU,那就是淘汰key2。

但是这种场景下,一般我们会期望按频率来进行淘汰,于是就有了LFU

Redis的LFU

策略

我们之前提供Redis的RedisObject中有个字段是lru,其实LFU也是用这个字段来实现的。

LFU和LRU两种策略是一种互斥关系,是不会同时开启的。LFU将这个字段(lru字段24位)拆分为两部分,高16位存储ldt,也就是Last Decrement Time,低8位存储logc logistic Counter。

image-20240823110848798

一个key是否活跃是根据这两个字段决定的。如果一个key,原来访问计数是255,但是一年没访问就会变成0(举例,并不一定是这样);如果一个key,频频被访问,它的访问计数会逐渐变大。

源码中,有部分代码证明了LFU和LRU是共用lru字段的。

#define LFU_INIT_VAL 5
robj *createObject(int type, void *ptr) {robj *o = zmalloc(sizeof(*o));//...//o的初始化//...if (server.maxmemory_policy & MAXMEMORY_FLAG_LFU) {o->lru = (LFUGetTimeMinutex()<<8) | LFU_INIT_VAL;}  else {o->lru = LRU_CLOCK();}return o'
}

代码中也说了,如果达到maxmemory,并且策略为LFU,前16位设置时间戳,后8位设置访问频率;否则设置lru当前时间戳。

访问频率衰减

redis.conf文件中,有一项配置为lfu_decay_time,默认值为1,表示每一分钟衰减一次。也就是上一次访问时间距离当前时间的分钟数。

lfu_decay_time 1
频率更新

访问计数的频率是一定概率增加的,次数不到5次,一定增加;如果大于5,小于255,一定概率增加;次数越大,越困难。

当然这也是有配置项的,lfu_log_factor被设置的越大,增加难度就越大,配置为0,每次会必然加1,上限为255(8位)。

void updateLFU(robj *val) {unsigned long counter = LFUDecrAndReturn(val);counter = LFULogIncr(counter);val->lru = (LFUGetTimeMinutes()<<8) | counter;
}

时间更新到高16位,次数更新到低8位。

这篇关于Redis内存淘汰的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1101782

相关文章

NameNode内存生产配置

Hadoop2.x 系列,配置 NameNode 内存 NameNode 内存默认 2000m ,如果服务器内存 4G , NameNode 内存可以配置 3g 。在 hadoop-env.sh 文件中配置如下。 HADOOP_NAMENODE_OPTS=-Xmx3072m Hadoop3.x 系列,配置 Nam

零基础学习Redis(10) -- zset类型命令使用

zset是有序集合,内部除了存储元素外,还会存储一个score,存储在zset中的元素会按照score的大小升序排列,不同元素的score可以重复,score相同的元素会按照元素的字典序排列。 1. zset常用命令 1.1 zadd  zadd key [NX | XX] [GT | LT]   [CH] [INCR] score member [score member ...]

两个月冲刺软考——访问位与修改位的题型(淘汰哪一页);内聚的类型;关于码制的知识点;地址映射的相关内容

1.访问位与修改位的题型(淘汰哪一页) 访问位:为1时表示在内存期间被访问过,为0时表示未被访问;修改位:为1时表示该页面自从被装入内存后被修改过,为0时表示未修改过。 置换页面时,最先置换访问位和修改位为00的,其次是01(没被访问但被修改过)的,之后是10(被访问了但没被修改过),最后是11。 2.内聚的类型 功能内聚:完成一个单一功能,各个部分协同工作,缺一不可。 顺序内聚:

JVM内存调优原则及几种JVM内存调优方法

JVM内存调优原则及几种JVM内存调优方法 1、堆大小设置。 2、回收器选择。   1、在对JVM内存调优的时候不能只看操作系统级别Java进程所占用的内存,这个数值不能准确的反应堆内存的真实占用情况,因为GC过后这个值是不会变化的,因此内存调优的时候要更多地使用JDK提供的内存查看工具,比如JConsole和Java VisualVM。   2、对JVM内存的系统级的调优主要的目的是减少

JVM 常见异常及内存诊断

栈内存溢出 栈内存大小设置:-Xss size 默认除了window以外的所有操作系统默认情况大小为 1MB,window 的默认大小依赖于虚拟机内存。 栈帧过多导致栈内存溢出 下述示例代码,由于递归深度没有限制且没有设置出口,每次方法的调用都会产生一个栈帧导致了创建的栈帧过多,而导致内存溢出(StackOverflowError)。 示例代码: 运行结果: 栈帧过大导致栈内存

Redis中使用布隆过滤器解决缓存穿透问题

一、缓存穿透(失效)问题 缓存穿透是指查询一个一定不存在的数据,由于缓存中没有命中,会去数据库中查询,而数据库中也没有该数据,并且每次查询都不会命中缓存,从而每次请求都直接打到了数据库上,这会给数据库带来巨大压力。 二、布隆过滤器原理 布隆过滤器(Bloom Filter)是一种空间效率很高的随机数据结构,它利用多个不同的哈希函数将一个元素映射到一个位数组中的多个位置,并将这些位置的值置

理解java虚拟机内存收集

学习《深入理解Java虚拟机》时个人的理解笔记 1、为什么要去了解垃圾收集和内存回收技术? 当需要排查各种内存溢出、内存泄漏问题时,当垃圾收集成为系统达到更高并发量的瓶颈时,我们就必须对这些“自动化”的技术实施必要的监控和调节。 2、“哲学三问”内存收集 what?when?how? 那些内存需要回收?什么时候回收?如何回收? 这是一个整体的问题,确定了什么状态的内存可以

NGINX轻松管理10万长连接 --- 基于2GB内存的CentOS 6.5 x86-64

转自:http://blog.chinaunix.net/xmlrpc.php?r=blog/article&uid=190176&id=4234854 一 前言 当管理大量连接时,特别是只有少量活跃连接,NGINX有比较好的CPU和RAM利用率,如今是多终端保持在线的时代,更能让NGINX发挥这个优点。本文做一个简单测试,NGINX在一个普通PC虚拟机上维护100k的HTTP

Lua 脚本在 Redis 中执行时的原子性以及与redis的事务的区别

在 Redis 中,Lua 脚本具有原子性是因为 Redis 保证在执行脚本时,脚本中的所有操作都会被当作一个不可分割的整体。具体来说,Redis 使用单线程的执行模型来处理命令,因此当 Lua 脚本在 Redis 中执行时,不会有其他命令打断脚本的执行过程。脚本中的所有操作都将连续执行,直到脚本执行完成后,Redis 才会继续处理其他客户端的请求。 Lua 脚本在 Redis 中原子性的原因

PHP原理之内存管理中难懂的几个点

PHP的内存管理, 分为俩大部分, 第一部分是PHP自身的内存管理, 这部分主要的内容就是引用计数, 写时复制, 等等面向应用的层面的管理. 而第二部分就是今天我要介绍的, zend_alloc中描写的关于PHP自身的内存管理, 包括它是如何管理可用内存, 如何分配内存等. 另外, 为什么要写这个呢, 因为之前并没有任何资料来介绍PHP内存管理中使用的策略, 数据结构, 或者算法. 而在我们