最频繁访问驻留缓存算法

标签: 访问 缓存 算法 | 发表时间:2017-01-26 15:39 | 作者:
出处:http://www.iteye.com

在搜索系统中,如何缓存搜索最频繁的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
 
 
 
 
 


已有 0 人发表留言,猛击->> 这里<<-参与讨论


ITeye推荐



相关 [访问 缓存 算法] 推荐:

最频繁访问驻留缓存算法

- - ITeye博客
在搜索系统中,如何缓存搜索最频繁的1000个搜索结果. 自定制的精准短文本搜索服务项目代码. 本文利用了ConcurrentHashMap和AtomicLong实现了线程安全且支持高并发的最频繁访问驻留缓存算法,除了缓存功能,还提供了缓存状态查询接口,非常实用. 比如,在搜索管理界面可看到如下缓存状态:.

缓存算法

- lostsnow - 小彰
没有人能说清哪种缓存算法由于其他的缓存算法. (以下的几种缓存算法,有的我也理解不好,如果感兴趣,你可以Google一下  ). 大家好,我是 LFU,我会计算为每个缓存对象计算他们被使用的频率. 我是LRU缓存算法,我把最近最少使用的缓存对象给踢走. 我总是需要去了解在什么时候,用了哪个缓存对象.

HTTP缓存算法

- - PHP源码阅读,PHP设计模式,PHP学习笔记,项目管理-胖胖的空间
HTTP协议缓存的目标是去除许多情况下对于发送请求的需求和去除许多情况下发送完整请求的需求. 以不发送请求或减少请求传输的数据量来优化整个HTTP架构,此目标的实现可以产生如下好处:. 降低对原始服务器的请求量. 减少了传送距离,降低了因为距离而产生的时延. 缓存基本处理过程包括七个步骤. 接收 – 缓存从网络中读取抵达的请求报文.

缓存相关——缓存穿透、缓存并发、缓存失效、缓存预热、缓存雪崩、缓存算法

- - 编程语言 - ITeye博客
我们在项目中使用缓存通常都是先检查缓存中是否存在,如果存在直接返回缓存内容,如果不存在就直接查询数据库然后再缓存查询结果返回. 这个时候如果我们查询的某一个数据在缓存中一直不存在,就会造成每一次请求都查询DB,这样缓存就失去了意义,在流量大时,可能DB就挂掉了. 要是有人利用不存在的key频繁攻击我们的应用,这就是漏洞.

缓存、缓存算法和缓存框架简介

- - 博客 - 伯乐在线
英文原文: jtraining,译文: Lixiang. 我们都听过 cache,当你问他们是什么是缓存的时候,他们会给你一个完美的答案,可是他们不知道缓存是怎么构建的,或者没有告诉你应该采用什么标准去选择缓存框架. 在这边文章,我们会去讨论缓存,缓存算法,缓存框架以及哪个缓存框架会更好. “缓存就是存贮数据(使用频繁的数据)的临时地方,因为取原始数据的代价太大了,所以我可以取得快一些.

android如何让webview里的资源访问本地缓存

- - 移动开发 - ITeye博客
继承并实现一个ContentProvider. 注册AndroidManifest.xml. 已有 0 人发表留言,猛击->> 这里<<-参与讨论. —软件人才免语言低担保 赴美带薪读研.

一种基于哨兵的缓存访问策略

- - 小火箭
学习自 一种基于“哨兵”的分布式缓存设计. 通常的缓存访问如下,箭头表示访问量,且为同一时刻访问. 如果Redis缓存命中,那么web就不会访问数据库,否则,客户端有N个并发请求就会有N个对数据库的并发请求,伴随而来的可能会是N个Redis SET操作. 为了消除这种情况下多余的请求,减轻数据库压力,引入一个“哨兵”请求,即当缓存不命中时,只有一个请求能落到数据库上,其余请求等待缓存更新.

日访问量百亿级的应用如何做缓存架构设计

- - IT瘾-dev
中生代技术链接技术大咖,分享技术干货. 链接3000+技术总监/CTO, 每天早上推送技术干货文章. 微博日活跃用户1.6亿+,每日访问量达百亿级,面对庞大用户群的海量访问,良好架构且不断改进的缓存体系具有非常重要的支撑作用. 4月21日,中生代技术走进盒子科技的现场技术交流活动上,新浪微博技术专家陈波为大家讲解了微博Cache架构的设计实践过程.

Hibernate 缓存

- - ITeye博客
1数据缓存:(date caching) 是一种将数据暂时存于内存缓存去中的技术,缓存通常是影响系统性能的关键因素. 2.ORM的数据缓存策略有3中.   1.事务级缓存:  分为 数据库事务和 应用级事务,是基于Session的生命周期的实现,每个session都会在内部维持一个数据缓存, 随session的创建和消亡.