Redis -LRU

当 Redis 内存超出物理内存时,内存的数据会开始喝磁盘产生频繁的交换(swap)交换会让 Redis 的性能急剧下降,对于访问量比较频繁的 Redis 来说这样龟速的存取效率基本上等于不可用。

在生产环境下我们是不允许 Redis 出现交互行为的,为了限制最大使用内存,Redis 提供了配置参数 maxmemory 来限制最大内存

当实际内存超出 maxmemory 时,Redis 提供了集中可选策略

策略 介绍
no-eviction 当内存不足以容纳新写入数据时,新写入操作会报错,无法写入新数据,一般不采用。
allkeys-lru 当内存不足以容纳新写入数据时,移除最近最少使用的key,这个是最常用的
allkeys-random 当内存不足以容纳新写入的数据时,随机移除key
volatile-lru 当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,移除最近最少使用的key(这个一般不太合适)
volatile-random 当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,随机移除某个key
volatile-ttl 当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,有更早过期时间的key优先移除

选择正确的回收策略是非常重要的,这取决于你的应用的访问模式,不过你可以在运行时进行相关的策略调整,并且监控缓存命中率和没命中的次数,通过Redis INFO 命令输出以便调优。

如果你只是使用 Redis 做缓存,那你就应该使用 allkeys-lru | allkeys-random,如果你还想同时使用持久化的功能,那就使用 volatile-lru | volatile-random 它们是永久的 key 不会被 LRU 算法淘汰。

回收是如何工作的

  1. 一个客户端运行了新的命令,添加新的数据
  2. Redis 检查内存使用情况,如果大于 maxmemory 的限制,则根据设定好的策略回收
  3. 一个新的命令被执行,等等

所以我们不断地越过内存限制的边界,越过它,然后通过驱逐键返回到限制之下。

如果某个命令导致使用大量内存(例如存储到新键中的大集合交集)一段时间,内存限制可能会被明显超过。

LRU 算法

实现 LRU 算法除了需要 key/value 字典外,还需要附加一个链表,链表中的元素按照一定的顺序进行排列。当空间满的时候,会踢掉链表尾部的元素,当字典的某个元素被访问时,它在链表中的位置会被移动到表头,所以链表的元素排列顺序就是元素最近被访问的时间顺序。

位于链表尾部的元素就是不被重用的元素,所以会被踢掉,位于表头的元素就是最近被人用过的元素,所以暂时不会踢

利用 Java 的 LinkedHashMap 来实现 LRU 算法

class LRUCache {class Node{Object key;Object value;Node pre;Node next;public Node(Object key, Object value){this.key = key;this.value = value;}}int capacity;HashMap<Object, Node> map = new HashMap<Object, Node>();Node head=null;Node end=null;public LRUCache(int capacity) {this.capacity = capacity;}public Object get(Object key){if(map.containsKey(key)){Node n = map.get(key);remove(n);setHead(n);return n.value;}return -1;}public void remove(Node n){if(n.pre !=null){n.pre.next = n.next;}else{head = n.next;}if(n.next!=null){n.next.pre = n.pre;}else{end = n.pre;}}public void setHead(Node n){n.next = head;n.pre = null;if(head!=null) {head.pre = n;}head = n;if(end ==null){end = head;}}public void set(Object key, Object value) {if(map.containsKey(key)){Node old = map.get(key);old.value = value;remove(old);setHead(old);}else{Node created = new Node(key, value);if(map.size()>=capacity){map.remove(end.key);remove(end);setHead(created);}else{setHead(created);}map.put(key,created);}}@Overridepublic String toString(){StringBuilder sb = new StringBuilder();Node node = head;while(node != null){sb.append(String.format("%s:%s ", node.key,node.value));node = node.next;}return sb.toString();}public static void main(String[] args) {LRUCache lruCache = new LRUCache(3);lruCache.set("1","123");lruCache.set("2","123");lruCache.set("3","123");System.out.println(lruCache.toString());lruCache.set("4","123");System.out.println(lruCache.toString());}}

近似 LRU 算法

Redis 使用的是一种近似 LRU 算法,他跟 LRU 算法还不太一样,只所以不使用 LRU 算法,是因为需要消耗大量额外的内存,需要对现有的数据结构进行很大的改造,近似 LRU 算法则很简单,在现有数据结构的基础上采用随机采样法来淘汰元素,能达到和 LRU 算法非常近似的效果,Redis 为实现近似算法,他给每个 key 增加了个额外的小字段,这个字段的长度是 24个bit,也就是最后一次被访问的时间戳

Redis LRU有个很重要的点,你通过调整每次回收时检查的采样数量,以实现调整算法的精度。这个参数可以通过以下的配置指令调整:

maxmemory-samples 5

Redis为什么不使用真实的LRU实现是因为这需要太多的内存。不过近似的LRU算法对于应用而言应该是等价的。使用真实的LRU算法与近似的算法可以通过下面的图像对比。

你可以看到三种点在图片中, 形成了三种带.

  • 浅灰色带是已经被回收的对象。
  • 灰色带是没有被回收的对象。
  • 绿色带是被添加的对象。

从图中可以看出采样数量越大,近似 LRU 算法的效果越接近严格 LRU 算法。同时 Redis3.0 在算法中增加了淘汰池,进一步提升了近似 LRU 算法的效果。

淘汰池是一个数组,它的大小是 maxmemory_samples,在每一次淘汰循环中,新随机出来的 key 列表会和淘汰池中的 key 列表进行融合,淘汰掉最旧的一个 key 之后,保留剩余较旧的 key 列表放入淘汰池中留待下一个循环。

Redis - LRU相关推荐

  1. Redis LRU 淘汰原理

    思考(作业):基于一个数据结构做缓存,怎么实现LRU--最长时间不被访问的元素在超过容量时删除? 问题:如果基于传统LRU 算法实现Redis LRU 会有什么问题? 需要额外的数据结构存储,消耗内存 ...

  2. 十二、Redis LRU算法详述(Least Recently Used - 最近最少使用)

    简介 Redis是基于内存存储的 key-value 数据库.我们都知道,内存虽然快但空间大小有限,当物理内存达到上限时,系统就会跑的很慢,这是因为 swap 机制会将部分内存的数据转移到swap分区 ...

  3. redis lru和lfu的实现

    在redis的lru的实现与传统的lru实现不同. 具体实现在evict.c文件中,当redis需要通过释放缓存的key来释放空间时,将会通过ecict.c的freeMemoryIfNeeded()函 ...

  4. Redis LRU算法

    一.配置Redis内存淘汰策略 maxmemory 100mbmaxmemory-policy allkeys-lrumaxmemory-samples 5 注意:Redis的LRU算法并非完整的实现 ...

  5. Redis的LRU算法

    2019独角兽企业重金招聘Python工程师标准>>> 整理自官方文档:将redis当做使用LRU算法的缓存来使用 当Redis被当做缓存来使用,当你新增数据时,让它自动地回收旧数据 ...

  6. 将redis当做使用LRU算法的缓存来使用

    当Redis被当做缓存来使用,当你新增数据时,让它自动地回收旧数据是件很方便的事情.这个行为在开发者社区非常有名,因为它是流行的memcached系统的默认行为. LRU是Redis唯一支持的回收方法 ...

  7. java 最少使用(lru)置换算法_「Redis源码分析」Redis中的LRU算法实现

    如果对我的文章感兴趣.希望阅读完可以得到你的一个[三连],这将是对我最大的鼓励和支持. LRU是什么 LRU(least recently used)是一种缓存置换算法.即在缓存有限的情况下,如果有新 ...

  8. node怎么把token放到redis_从零开始手写 redis(八)朴素 LRU 淘汰算法性能优化

    前言 java从零手写实现redis(一)如何实现固定大小的缓存? java从零手写实现redis(三)redis expire 过期原理 java从零手写实现redis(三)内存数据如何重启不丢失? ...

  9. Redis精通系列——LRU算法详述(Least Recently Used - 最近最少使用)

      本文已收录于专栏 ❤️<Redis精通系列>❤️ 上千人点赞收藏,全套Redis学习资料,大厂必备技能! 目录 1.简介 2.maxmemory配置 3.内存达到maxmemory怎么 ...

最新文章

  1. linux虚拟化毕业设计,毕业设计(论文)-基于Linux的云校园桌面虚拟化系统的设计与实现(68页)-原创力文档...
  2. 阿里云成为首个通过《面向公有云模式的政务云服务》测评的厂商
  3. DHCP***的防御处理总结
  4. 工程师和科学家有什么区别
  5. java选课系统_java实现学生选课系统
  6. sqlserver备份和恢复
  7. VS2013 调用的目标发生了异常
  8. Canvas快速入门知识点
  9. 如果浏览器大战的格局改变会怎样?
  10. 错误解决:java.util.concurrent.ExecutionException: org.apache.catalina.LifecycleException: Failed to star
  11. 药物临床试验数据递交FDA的规定
  12. 上位机PC控制UR3机器人实现方式
  13. html 设计尺寸,多少像素才合适 网页设计标准尺寸大讲解
  14. 研究团队再次发现安全漏洞,微软警告Azure用户
  15. java程序读取文件_java,编写一个程序,可以读取文件数据
  16. 由置换反应引发的思考
  17. 英语单词APP开发功能需求
  18. 风车网陈晓峰回忆录:我的两个月倒闭史
  19. 平安产险深圳分公司:绿色保险亮相第十五届深圳国际金融博览会
  20. 记录阿里云 centOS FRP 树莓派 内网穿透

热门文章

  1. python实现Diffie-Hellman密钥交换算法
  2. 理光有邮件服务器吗,理光Aficio 3035复印机通过电子邮件发送扫描文件的设定方法及操作步骤...
  3. 求助,为什么苹果系统安装了endnote了WPS检测不到
  4. Redis 的各项功能帮助我们解决了哪些问题?
  5. 如何在vscode配置php开发环境
  6. 机器学习之决策树算法详解
  7. ubuntu nsight eclipse 打不开
  8. 对抗生成网络(GAN)学习笔记
  9. HUAWEI华为笔记本电脑荣耀MagicBook Pro i5(HBL-W19)HONOR原装出厂Windows10系统恢复原厂OEM系统1809
  10. 搜索引擎爬虫蜘蛛的useragent