当前位置:
科技动态 > 【第93期】经典面试题:Redis内存满了怎么办?
【第93期】经典面试题:Redis内存满了怎么办?
2022年5月17日下午3:48
•
面试问题 •
阅读 4
点击上方“Java面试题精选”,关注公众号
面试时画图,查漏补缺
>>额外编号:以前的面试题,10道为单位,放在这个公众号菜单栏->面试题中。如果您需要它们,欢迎您阅读。
Redis占用内存大小
我们知道Redis是一个基于内存的键值数据库。由于系统的内存大小是有限的,我们在使用Redis时可以配置Redis可以使用的最大内存大小。
1.通过配置文件进行配置
设置内存大小,在Redis安装目录下的redis.conf配置文件中添加如下配置。
//设置Redis最大内存大小为100Mmaxmemory 100mb
redis配置文件不一定使用安装目录下的redis.conf文件。启动redis服务时,可以传递一个参数来指定redis配置文件。
2.通过命令修改
Redis支持运行时通过命令动态修改内存大小
//设置Redis占用的最大内存大小为100M127.0.0.1:6379> config set maxmemory 100mb//获取Redis可以使用的最大内存大小 127.0.0.1:6379> config get maxmemory
如果不设置最大内存大小或者将最大内存大小设置为0,则64位操作系统下对内存大小没有限制,32位操作系统下最大可以使用3GB内存系统。
Redis内存过时
由于可以设置Redis的最大内存大小,因此存在配置的内存用完的情况。那么当内存耗尽的时候,我们继续往Redis中添加数据,不就没有内存可用了吗?
事实上,Redis定义了几种策略来处理这种情况:
noeviction(默认策略):不再为写请求提供服务,直接返回错误(DEL请求和一些特殊请求除外)
allkeys-lru:使用LRU算法从所有key中消除
volatile-lru:使用LRU算法淘汰设置了过期时间的key
allkeys-random:随机消除所有键中的数据
volatile-random:随机淘汰设置过期时间的keyvolatile-ttl:在设置了过期时间的key中,会根据key的过期时间进行淘汰。到期日越早,淘汰的优先级越高。
当使用 volatile-lru、volatile-random 和 volatile-ttl 三种策略时,如果无法消除任何 key,则会返回与 noeviction 相同的错误。
如何获取和设置内存淘汰策略
获取当前的内存消除策略:
127.0.0.1:6379> 配置获取最大内存策略
通过配置文件设置淘汰策略(修改redis.conf文件):
最大内存策略 allkeys-lru
通过命令修改淘汰策略:
127.0.0.1:6379> 配置集 maxmemory-policy allkeys-lru
LRU算法
什么是LRU?
上面提到,当Redis可用的最大内存用完时,可以使用LRU算法来淘汰内存。那么什么是LRU算法呢?
LRU(Leastmostlyused),即最近最少使用,是一种缓存替换算法。当使用内存作为缓存时,缓存的大小一般是固定的。当缓存满了,继续向缓存添加数据时,需要淘汰一些旧数据,释放内存空间来存储新数据。
这时候就可以使用LRU算法了。其核心思想是:如果一条数据在最近一段时间内没有被使用过,那么未来被使用的可能性很小,因此可以将其淘汰。
使用java实现一个简单的LRU算法
public class LRUCache { //容量 private int 容量; //统计当前有多少个节点 private int count; //缓存节点 private Map> nodeMap;私有节点头;私有节点尾部;public LRUCache(int capacity) { if (capacity < 1) { throw new IllegalArgumentException(String.valueOf(capacity)); } this.capacity = capacity; this.nodeMap = new HashMap<>(); //初始化头节点和尾节点,使用哨兵模式,减少判断头节点和尾节点是否为空的代码 Node headNode = new Node (null, null) ; 节点 tailNode = new Node(null, null); www.sychzs.cn = tailNode; tailNode.pre = headNode; this.head = headNode; this.tail = tailNode; }
public void put(k key, v value) { Node node = nodeMap.get(key); if (node == null) { if (count >=capacity) { 先删除一个节点removeNode() ; moveNodeToHead(节点);
public Node get(k key) { Node node = nodeMap.get(key); if (node != null) { moveNodeToHead(node);返回节点; }private voidremoveNode(){ 节点node = tail.pre; //从链表里面移除 removeFromList(node); nodeMap.remove(node.key);数数 - ; }
private void removeFromList(Node node) { Node pre = node.pre;节点下一个=www.sychzs.cn;
前.下一个 = 下一个;下一个.pre = pre;
节点.next = null;节点.pre = null; }
private void addNode(Node node) { //添加节点到头部 addToHead(node); nodeMap.put(node.key, 节点);计数++; }
private void addToHead(Nodenode){ Node next = www.sychzs.cn;下一个.pre=节点;节点.下一个 = 下一个;节点.pre = 头; www.sychzs.cn = 节点; }
public void moveNodeToHead(Node node) { //从链表里面移除 removeFromList(node); // 添加节点到头部 addToHead(node); }
类 Node { k 键; v 值;节点预;下一个节点;public Node(k key, v value) { this.key = key; this.value = 值; } }}
上面的代码实现了一个简单的LUR算法。代码很简单,有注释。只要仔细看就很容易理解。
Redis中LRU的实现
近似LRU算法
Redis采用近似LRU算法,与常规LRU算法不太一样。近似LRU算法通过随机采样来消除数据,每次随机选择5个(默认)key,并消除最近最少使用的key。
样本数量可以通过 maxmemory-samples 参数修改:
示例:最大内存样本 10
maxmenory-samples配置越大,淘汰结果越接近严格的LRU算法。
为了实现近似的LRU算法,Redis为每个key添加了一个额外的24位字段来存储该key最后一次被访问的时间。
Redis3.0近似LRU的优化
Redis3.0对近似LRU算法做了一些优化。新算法将维护一个候选池(大小为 16)。池中的数据按照访问时间排序。第一个随机选择的密钥将被放入池中。仅当访问时间小于池时,才会选择随后随机选择的每个密钥。最短时间将被放入池中,直到候选池满为止。当它满了的时候,如果有新的key需要放入,那么最后访问时间最长的(最近访问过的)就会从池中移除。
当需要淘汰时,只需从池中选择最近访问时间最小的key(最长时间没有被访问过的key)并淘汰即可。
LRU算法比较
我们可以通过实验来比较各个LRU算法的准确率。首先向Redis添加一定量的数据n以耗尽Redis的可用内存,然后向Redis添加n/2个新数据。这个时候就需要剔除一部分。如果对数据遵循严格的LRU算法,则应消除添加的前n/2个数据。
生成如下各个LRU算法的对比图
图片来源:www.sychzs.cn/a/1190000017555834
可以看到图中有三个不同颜色的点:
浅灰色为淘汰数据
灰色为尚未淘汰的旧数据
绿色为新添加的数据
我们可以看到,采样数为10的Redis3.0生成的图最接近严格LRU。并且使用相同数量的5个样本,Redis3.0也优于Redis2.8。LFU算法
LFU算法是Redis4.0中新添加的淘汰策略。它的全称是“LeastFrequentlyUsed”,其核心思想是根据键最近被访问的频率来淘汰。首先淘汰很少访问的密钥,并保留访问较多的密钥。
LFU算法可以更好地表示被访问的密钥的流行度。如果使用LRU算法,某个key长期没有被访问过,只是偶尔被访问过,那么它就被认为是热数据,不会被淘汰,而且有些key以后很可能会被访问到。被淘汰了。如果使用LFU算法就不会出现这种情况,因为使用一次并不会让某个key成为热点数据。
LFU 有两种策略:
volatile-lfu:使用LFU算法淘汰设置了过期时间的key中的key。
allkeys-lfu:使用LFU算法消除所有key中的数据
设置和使用这两种消除策略与之前提到的相同,但需要注意的是,这两周的策略只能在Redis 4.0及以上版本中设置。如果设置在Redis 4.0以下,会报错。
问题
最后我留下一个小问题。有些人可能注意到了,我在文章中并没有解释为什么Redis使用近似LRU算法而不是精确LRU算法。您可以在评论区给出您的答案,让我们一起讨论学习。
参考
https://www.sychzs.cn/topics/lru-cache https://www.sychzs.cn/a/1190000016743562 https://www.sychzs.cn/a/1190000017555834
来源:www.sychzs.cn/post/5d674ac2e51d4557ca7fdd70
最后五期
【第88期】面试官问:能告诉我spring中接口bean是怎么注入的吗?
【第89期】面试官5问一个TCP连接可以发送多少个HTTP请求?
【第90期】面试官:说说用Redis实现大规模帖子浏览量统计的想法。
【第91期】面试官:Spring使用了哪些设计模式?就说三种
【第92期】面试官:你说你精通Java并发,那给我讲讲J.U.C吧。
而不是在网上搜索问题?还不赶快关注我们吧~版权声明:本文内容由网友自愿贡献,本文所表达的观点仅代表作者自己的观点。本网站仅提供信息存储空间服务,不拥有任何所有权,也不承担相关法律责任。如果您发现本站有任何涉嫌侵权/非法内容,请发送邮件举报。一经核实,该网站将立即删除。
本文由斑马博客整理。本文链接为:https://www.sychzs.cn/index.php/post/6876.html