文档章节

最频繁访问驻留缓存算法

杨尚川
 杨尚川
发布于 2016/07/18 16:06
字数 672
阅读 735
收藏 39

在搜索系统中,如何缓存搜索最频繁的1000个搜索结果?自定制的精准短文本搜索服务项目代码

 

本文利用了ConcurrentHashMap和AtomicLong实现了线程安全且支持高并发的最频繁访问驻留缓存算法,除了缓存功能,还提供了缓存状态查询接口,非常实用。

比如,在搜索管理界面可看到如下缓存状态:


缓存状态

最大缓存数量: 1000
当前缓存数量: 11
驱逐缓存次数: 0
命中缓存次数: 6
未命中缓存次数: 11
缓存命中比例: 35.294117 %

 

搜索缓存命中情况(11)

序号 搜索关键词 缓存命中次数
1 L 3
2 LYB 2
3 LY 1
4 LANGYB 0
5 X 0
6 LANG 0
7 XL 0
8 LAN 0
9 XLF 0
10 LANGY 0
11 XLFD 0

 

 实现代码如下:

import java.util.Collections;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;

/**
 * 最频繁访问驻留缓存算法
 * Created by ysc on 7/18/16.
 */
public class ConcurrentLRUCache<K, V> {
    private int maxCacheSize;
    private Map<K, CacheItem<V>> cache = new ConcurrentHashMap<>();
    private AtomicLong totalEvictCount = new AtomicLong();
    private AtomicLong hitCount = new AtomicLong();
    private AtomicLong notHitCount = new AtomicLong();

    public ConcurrentLRUCache(int maxCacheSize) {
        cache = new ConcurrentHashMap<>(maxCacheSize, 1, 10);
        this.maxCacheSize = maxCacheSize;
    }

    public String getStatus(){
        StringBuilder status = new StringBuilder();

        long total = hitCount.get()+notHitCount.get();

        status.append("最大缓存数量: ").append(maxCacheSize).append("\n")
                .append("当前缓存数量: ").append(getActualCacheSize()).append("\n")
                .append("驱逐缓存次数: ").append(totalEvictCount.get()).append("\n")
                .append("命中缓存次数: ").append(hitCount.get()).append("\n")
                .append("未命中缓存次数: ").append(notHitCount.get()).append("\n")
                .append("缓存命中比例: ").append(total == 0 ? 0 : hitCount.get()/(float)total*100).append(" %\n");

        return status.toString();
    }

    public String getKeyAndHitCount(){
        StringBuilder status = new StringBuilder();
        AtomicInteger i = new AtomicInteger();

        cache.entrySet().stream().sorted((a,b)->b.getValue().getCount()-a.getValue().getCount()).forEach(entry->status.append(i.incrementAndGet()).append("\t").append(entry.getKey()).append("\t").append(entry.getValue().getCount()).append("\n"));

        return status.toString();
    }

    public int getMaxCacheSize() {
        return maxCacheSize;
    }

    public int getActualCacheSize() {
        return cache.size();
    }

    public Map<K, CacheItem<V>> getCache() {
        return Collections.unmodifiableMap(cache);
    }

    public AtomicLong getTotalEvictCount() {
        return totalEvictCount;
    }

    public long getHitCount() {
        return hitCount.longValue();
    }

    public long getNotHitCount() {
        return notHitCount.longValue();
    }

    public void put(K key, V value){
        if(cache.size() >= maxCacheSize){
            // evict count
            int evictCount = (int)(maxCacheSize*0.1);
            if(evictCount < 1){
                evictCount = 1;
            }
            totalEvictCount.addAndGet(evictCount);
            cache.entrySet().stream().sorted((a,b)->a.getValue().getCount()-b.getValue().getCount()).limit(evictCount).forEach(entry->cache.remove(entry.getKey()));
            return;
        }
        cache.put(key, new CacheItem<V>(value, new AtomicInteger()));
    }

    public V get(K key){
        CacheItem<V> item = cache.get(key);
        if(item != null){
            item.hit();
            hitCount.incrementAndGet();
            return item.getValue();
        }
        notHitCount.incrementAndGet();
        return null;
    }

    private static class CacheItem<V>{
        private V value;
        private AtomicInteger count;

        public CacheItem(V value, AtomicInteger count) {
            this.value = value;
            this.count = count;
        }

        public void hit(){
            this.count.incrementAndGet();
        }

        public V getValue() {
            return value;
        }

        public int getCount() {
            return count.get();
        }
    }

    public static void main(String[] args) {
        ConcurrentLRUCache<Integer, Integer> cache = new ConcurrentLRUCache<>(5);
        for(int i=0; i<9; i++){
            cache.put(i, i);
            if(i%2==0){
                for(int j=0; j<i+2; j++){
                    cache.get(i);
                }
            }
        }
        System.out.println(cache.getStatus());
        System.out.println(cache.getKeyAndHitCount());
    }
}

运行代码控制台输出如下:

最大缓存数量: 5
当前缓存数量: 5
驱逐缓存次数: 2
命中缓存次数: 30
未命中缓存次数: 0
缓存命中比例: 100.0 %

1	8	10
2	6	8
3	4	6
4	2	4
5	0	2

 

© 著作权归作者所有

杨尚川

杨尚川

粉丝 1103
博文 220
码字总数 1624053
作品 12
东城
架构师
私信 提问
深入理解Ehcache系列(三)

在系列(一)中,提到Ehcache提供了三种清空策略.那么如何设置相应的参数呢? Ehcache提供了配置文件的方式,也提供了参数传递的方式. 配置文件src/config/cache.xml <cache name="testCache"max...

ZooKeeper
2013/12/17
2.4K
3
Salesforce和SAP HANA的元数据访问加速

Salesforce 在Jerry的其他文章曾经提到,Salesforce里运行时对象均是通过静态存储的元数据,经过Runtime engine加工而成的。 Because metadata is a key ingredient of Force.com applicatio...

JerryWang_SAP
02/14
8
0
Lookup 组件用法全解

Lookup是查找的意思,Lookup组件实现两个数据源的连接,和Join语句实现的功能类似,使用Lookup 组件需要配置: 两个输入:一个是上游数据流的输入Source Table,一个是要查找的数据集 Lookup...

长征6号
2016/03/16
0
0
Android中常见的内存泄漏 memory leak

四种主要的情况: 1、Activity对象未被回收 2、集合对象造成的泄漏 3、资源对象没关闭造成内存泄漏 4、使用对象池避免频繁创建对象 ------------------------------------------------------...

MonkeySun
2016/11/14
187
0
页面置换算 - FIFO、LFU、LRU

缓存算法(页面置换算法)-FIFO、LFU、LRU   在前一篇文章中通过leetcode的一道题目了解了LRU算法的具体设计思路,下面继续来探讨一下另外两种常见的Cache算法:FIFO、LFU 1.FIFO算法   ...

孟宇
2015/12/17
0
0

没有更多内容

加载失败,请刷新页面

加载更多

Spring Boot + Mybatis + Ehcache 二级缓存实例

二级缓存是多个SqlSession共享的,其作用域是mapper的同一个namespace,不同的sqlSession两次执行相同namespace下的sql语句且向sql中传递参数也相同即最终执行相同的sql语句,第一次执行完毕...

xiaolyuh
12分钟前
4
0
Spring源码学习(二)哎呦,按菜谱做菜与AbstractAutowireCapableBeanFactory.createBean流程差不多

记得跟老婆谈恋爱时,有一天心血来潮给老婆做饭,按照菜谱一步一步的做,结果差点把厨房烧了!!! 这事至今老婆还记得。 入口 上一篇说了,AbstractBeanFactory.getBean的主流程 ,今天来说下...

温安适
14分钟前
34
0
前端UI攻城狮 你们该抛弃jQuery了

你不再需要jQuery! Web工程师太依赖jQuery了,某种意义上说jQuery已经成了JavaScript的同义词。但是我们真的需要他么?或许我们应该反思一下什么时候才真的需要jQuery。 对我个人而言开始使...

前端老手
15分钟前
3
0
六、Java设计模式之工厂方法

工厂方法定义: 定义一个创建对象的接口,但让实现这个接口的类来决定实例化哪个类,工厂方法让类的实例化推迟到子类中进行 类型:创建型 工厂方法-使用场景: 创建对象需要大量重复的代码 ...

东风破2019
57分钟前
5
0
win服务器管理遇到的一系列问题记录

有些小伙伴在使用iis7远程桌面管理工具的时候总是会遇到一系列的问题,下面就是为大家介绍一下服务器日常管理过程中出现的问题及我的解决办法和心得。希望能帮到大家。   拒绝服务器重新启...

1717197346
今天
6
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部