`

最频繁访问驻留缓存算法

阅读更多

在搜索系统中,如何缓存搜索最频繁的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
 
 
 
 
 
2
0
分享到:
评论

相关推荐

    操作系统页面置换算法实现,fifo、lfu、lru、opt,界面由MFC实现

    这些算法处理的主要问题是缓存(或称为页面)不足时,如何选择应该被替换出内存的页面。本项目实现了一个模拟器,用于演示和比较四种不同的页面置换算法:FIFO(先进先出)、LFU(最不常用)、LRU(最近最少使用)...

    内存替换算法各个算法对比

    LFU在处理短期爆发性访问的页面时表现良好,但对长期低频但突然大量使用的页面可能会造成误判,这些页面可能会因为早期的低频使用而在内存中长期驻留。 3. **DBL (2Q or Double-Queue)** DBL是LRU和LFU的结合,它...

    android端用于异步加载图片,内存缓存,文件缓存,imageview显示图片时增加淡入淡出动画。.rar

    内存缓存是快速访问图片的关键,尤其是在频繁滚动列表时。Android中可以使用LRU(Least Recently Used)算法实现,通过维护一个最大容量的HashMap,当内存不足时,优先移除最近最少使用的图片。这样可以确保常用...

    19 MySQL是如何将LRU链表的使用性能优化到极致的.pdf

    通常,一旦缓存页被访问,就会将其移动到LRU链表头部,以延长其在内存中驻留的时间。但是,如果经常访问的缓存页频繁移动,会导致链表节点的大量移动,影响性能。因此,MySQL的优化措施是,只有热数据区域的后3/4...

    External Memory Data Structures

    - 使用缓存替换策略,如LRU(最近最少使用)算法,确保最常使用的数据驻留在缓存中。 3. **I/O复杂性的分析与优化**: - 分析数据结构操作所需的磁盘读写次数,作为评估其效率的关键指标。 - 设计算法时,优先...

    大规模游戏社交网络节点相似性算法及其应用-3-5 腾讯 Alluxio 加速下一代大数据业务落地.zip

    1. **性能提升**:通过将频繁访问的数据放在内存,Alluxio减少了数据读取的延迟,加速了节点相似性算法的计算过程。 2. **资源优化**:通过智能缓存策略,Alluxio避免了重复计算,减少了不必要的资源消耗。 3. **...

    模拟操作系统的页面置换

    这种算法简单但可能导致频繁的替换。 4. **最近最不常用(NM, Not Recently Used)**:与LRU相反,选择最近最不常用的页,但它需要更多的硬件支持来记录每个页的使用情况。 5. **Clock算法**:一种简单而有效的算法...

    基于节点动态内容流行度的缓存管理策略

    通过缓存分层(主缓存和副缓存),策略能够有效地识别和保护流行内容,延长其在缓存中的驻留时间。 4. **主缓存(Primary Cache, PC)与副缓存(Secondary Cache, SC)**:主缓存用于识别流行内容,而副缓存则用于...

    行业-16 简单的LRU链表在Buffer Pool实际运行中,可能导致哪些问题.rar

    如果数据库的工作集(即频繁访问的数据页集合)超过了Buffer Pool的大小,那么即使使用LRU,也无法保证所有常用的数据页都驻留在内存中。这会导致频繁的页面替换,增加了不必要的I/O操作,从而降低了系统性能。 ...

    操作系统存储管理LRU

    操作系统中的存储管理是确保系统高效运行的关键组成部分,LRU(Least Recently Used)是最常见的页面替换算法之一,用于解决内存不足时的数据处理问题。LRU算法的核心思想是:最近最少使用的页面优先被淘汰。当物理...

    操作系统 虚拟存储器

    虚拟存储器系统通常与缓存紧密结合,缓存中存储的是频繁访问的页面,以减少对主存的访问。 8. **虚拟存储器的扩展性**: 通过虚拟地址空间,操作系统可以支持多进程并发,每个进程都有独立的地址空间,避免了地址...

    Cache-optimization:调整循环以在 C 中生成对缓存更友好的代码

    优化代码的目标之一就是尽量让数据驻留在L1缓存中,减少访问更慢的内存层次。 1. **循环展开**:循环展开是缓存优化的一个常见技巧,通过增大循环体,可以一次性加载更多的数据到缓存中。例如,如果你知道一个数组...

    memcached.pdf

    1. **减轻数据库压力**:将频繁访问的数据缓存到Memcached,减少对数据库的读写操作。 2. **session共享**:在分布式系统中,通过Memcached共享用户session,提高用户体验。 3. **页面缓存**:对静态页面或动态生成...

    os.rar_memory

    7. **缓存管理**:为了提高性能,操作系统还会利用CPU缓存(L1、L2、L3)来存放频繁访问的数据。缓存管理涉及到缓存替换策略和缓存一致性协议。 8. **内存池**:在某些情况下,操作系统或应用程序会使用内存池技术...

    最新操作系统第五版答案第8章复习题及习题解答.pdf

    8.8 FIFO(先进先出)页替换算法按照页面进入主存的顺序替换,而Clock算法类似,但会忽略已被访问过的页面,以减少近期频繁访问页面的替换。 8.9 页缓冲通过保留被替换出但近期可能再次访问的页,减少磁盘I/O,同时...

    第5章作业答案1

    3. 最近最久未使用(LRU)算法:替换最近最长时间未被访问的页面。 4. Clock置换算法:一种近似LRU的算法,使用位标记简化实现。 5. 最少使用算法:选择在内存中驻留时间最长的页面进行替换。 6. 页面缓冲算法:结合...

    计算机存储系统PPT学习教案.pptx

    例如,对于一个4块的全相联Cache,通过访问序列的不同,可以看到FIFO可能会导致频繁替换,而LRU则更能保持常用块的驻留。 接下来,讨论的是Cache的更新策略。在CPU执行写操作时,数据只写入Cache而不立即写入主存,...

    2008:操作系统(A)1

    9. **工作集(Working sets)**:工作集是进程最近使用的页面集合,用于优化页面替换算法,确保常用页面驻留在内存中。 10. **Inode**:在Unix-like系统中,Inode是用于存储文件元数据的数据结构,包含文件大小、...

    zhuanzhi.rar_矩阵乘法_矩阵乘法优化_矩阵优化 c

    1. **缓存优化**:利用CPU缓存的局部性原理,通过调整矩阵的存储顺序和计算顺序,使得频繁访问的数据能更好地驻留在高速缓存中,减少主内存访问,从而提高速度。 2. **并行计算**:将矩阵乘法任务分解为多个子任务...

Global site tag (gtag.js) - Google Analytics