专栏名称: ImportNew
伯乐在线旗下账号,专注Java技术分享,包括Java基础技术、进阶技能、架构设计和Java技术领域动态等。
目录
相关文章推荐
芋道源码  ·  大厂都在用的分库分表 12 种分片算法 ·  昨天  
芋道源码  ·  强类型查询:Java 的全能 ORM 神器 ·  4 天前  
芋道源码  ·  线程数突增!领导说再这么写就gc掉我.... ·  4 天前  
芋道源码  ·  这五款牛逼的 IDEA ... ·  1 周前  
51好读  ›  专栏  ›  ImportNew

最频繁访问驻留缓存算法

ImportNew  · 公众号  · Java  · 2017-03-08 19:42

正文

(点击上方公众号,可快速关注)


来源:杨尚川,

my.oschina.net/apdplat/blog/713896

如有好文章投稿,请点击 → 这里了解详情


在搜索系统中,如何缓存搜索最频繁的1000个搜索结果?自定制的精准短文本搜索服务项目代码(https://github.com/ysc/short-text-search/blob/master/src/main/java/org/apdplat/search/utils/ConcurrentLRUCache.java)。


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


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


缓存状态


最大缓存数量: 1000

当前缓存数量: 11

驱逐缓存次数: 0

命中缓存次数: 6

未命中缓存次数: 11

缓存命中比例: 35.294117 %


搜索缓存命中情况(11)



实现代码如下:


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 {

    private int maxCacheSize;

    private Map> 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> 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(value, new AtomicInteger()));

    }

 

    public V get(K key){

        CacheItem item = cache.get(key);

        if(item != null){

            item.hit();

            hitCount.incrementAndGet();

            return item.getValue();

        }

        notHitCount.incrementAndGet();

        return null;

    }

 

    private static class CacheItem{

        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 cache = new ConcurrentLRUCache<>(5);

        for(int i=0; i<9; i++){

            cache.put(i, i);

            if(i%2==0){

                for(int j=0; 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


看完本文有收获?请转发分享给更多人

关注「ImportNew」,看技术干货