lru 缓存机制 redis java jdk LinkedHashMap 有界队列实现

严格的lru算法

  1.基于读 更新时间, 2.并且要消除偶尔读对整体数据的影响. 也就是说要考虑 最近x小时内读的次数. 和 最近读的次数.

  实现上, 需要有个队列时刻维护好这个顺序关系. 但是redis没有这样实现, 避免内存浪费. 

  jdk这样实现了, 而且有个AccessOrder 来实现按读还是按写排序, AccessOrder=true按读.

redis的lru原理

   抽样算法 , 结合 pooling 缓存池技术

  详解Redis的LRU算法

那我能不能在下一轮移除Key时,利用好上一轮知晓的一些信息?

However if you think at this algorithm across its executions, you can see how we are trashing a lot of interesting data. Maybe when sampling the N keys, we encounter a lot of good candidates, but we then just evict the best, and start from scratch again the next cycle.

start from scratch太傻了。于是Redis又做出了改进:采用缓冲池(pooling)

当每一轮移除Key时,拿到了这个N个Key的idle time,如果它的idle time比 pool 里面的 Key的idle time还要大,就把它添加到pool里面去。这样一来,每次移除的Key并不仅仅是随机选择的N个Key里面最大的,而且还是pool里面idle time最大的,并且:pool 里面的Key是经过多轮比较筛选的,它的idle time 在概率上比随机获取的Key的idle time要大,可以这么理解:pool 里面的Key 保留了"历史经验信息"。

java 里的lru 

    LinkedHashMap removeEldestEntry 基于create/read的lru算法. 不是基于读的lru算法.

  accessOrder=true时

例子

  

public class Test {
    public static  class LruMap extends LinkedHashMap{

        public LruMap(){
            super(16,0.75f,true);
        }
        @Override
        protected boolean removeEldestEntry(Map.Entry eldest) {
            int size = size();
            return size >2;
        }
    }
    public static void main(String[] args) throws InterruptedException {

        Map map=new LruMap();

        map.put("1","1");
        map.put("2","2");

        Object o = map.get("1");
        System.out.println("after read key=1,value="+o+", map="+map.entrySet());

        map.put("3",3);

        System.out.println("after put key=3,value=3, map="+map.entrySet());

  }
}

输出结果

after read key=1,value=1, map=[2=2, 1=1]
after put key=3,value=3, map=[1=1, 3=3]

源代码解读

在这里插入图片描述

void afterNodeAccess(Node<K,V> e) { // move node to last
    LinkedHashMap.Entry<K,V> last;
    if (accessOrder && (last = tail) != e) {
        LinkedHashMap.Entry<K,V> p =
            (LinkedHashMap.Entry<K,V>)e, b = p.before, a = p.after;
        p.after = null;

        //将e从中间抽出来, 步骤1: 把e的a节点(after节点) 设置到e的b节点(before节点)上after上
        if (b == null)
            head = a;
        else
            b.after = a;
        //将e从中间抽出来,步骤2:  把e的e的b节点(before节点)上 设置到a节点(after节点) 的before上
        if (a != null)
            a.before = b;
        else
            last = b;
        
        // 将e节点放置到最末尾, 代表最新读取.
        if (last == null)
            head = p;
        else {
            p.before = last;
            last.after = p;
        }
        tail = p;
        ++modCount;
    }
}

accorder=false时,默认

 public class Test {

   public static  class LruMap extends LinkedHashMap{

        @Override
        protected boolean removeEldestEntry(Map.Entry eldest) {
            int size = size();
            return size >2;
        }
    }
    public static void main(String[] args) throws InterruptedException {

        Map map=new LruMap();

        map.put("1","1");
        map.put("2","2");

        Object o = map.get("1");
        System.out.println("after read key=1,value="+o+", map="+map.entrySet());

        map.put("3",3);

        System.out.println("after put key=3,value=3, map="+map.entrySet());


  }
}


after read key=1 ,value=1, map=[1=1, 2=2]
after put   key=3, value=3, map=[2=2, 3=3]

gava中的实现

剖析LinkedHashMap 和 LRU实现的种种_黄老师-的博客-CSDN博客_java lru

Guava LRU的具体实现

在Guava Cache的LRU实现中,它的双向链表并不是全局的。而是每个Segment(ConcurrentHashMap中的概念)中都有。

一共涉及到三个Queue其中包括:AccessQueue和WriteQueue,以及RecentQueue。其中AccessQueue和WriteQueue就是双向链表;而RecentQueue才是真正的Queue,它就是CocurrentLinkedQueue。

接下来我们简单分析下Guava Cache是如何通过这三个Queue来实现的LRU。

有界队列实现

   需要有个淘汰机制. 利用linkedHashMap可以实现

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值