java动态缓存——创建一个简单的缓存

在实际项目中,我们经常会需要使用到缓存。一般来说,缓存介质是内存;而常见的DB将数据存储在硬盘中的;缓存读取是电脉冲,而硬盘读取是机械地读取转动的硬盘,速度差异是上百倍的。所以往往可以通过缓存,对经常用到的数据进行访问,以提高速度。

        创建缓存实际上就是针对两个对象,1. Cache对象,即一个缓存对象;2. CacheManager对象,即一个管理不同缓存的对象,其核心实际上就是一个Map,用来保存与获取不同缓存。

      最简单的缓存 实现如下:

/** 
 *  项目名称: 
 *  文件说明:创建一个缓存管理器 <span style="font-family: Arial, Helvetica, sans-serif;">刘晨曦</span> 
 *  主要特点: 
 *  版本号:1.0 
 *  创建时间:2013-12-3 
 **/  
package NBOffer;  
  
import java.util.HashMap;  
  
public class CacheManager {  
  
    static HashMap<String,Cache> cacheMap=new HashMap<String,Cache>();  
      
    public static Cache getCache(String id)  
    {  
        return cacheMap.get(id);  
    }  
      
    public static void putCache(Cache cache)  
    {  
        cacheMap.put(cache.id, cache);  
    }  
      
    public static void main(String[] args) {  
        Cache cache1=new Cache("1","A1");  
        Cache cache2=new Cache("2","A2");  
        CacheManager.putCache(cache1);  
        CacheManager.putCache(cache2);  
        CacheManager.getCache("1").showInfo();  
    }  
}  
  
class Cache  
{  
    String id;//相当于主键  
    Object val;  
    public Cache(String id)  
    {  
        new Cache(id,null);  
    }  
      
    public Cache(String id,Object val)  
    {  
        this.id=id;  
        this.val=val;  
    }  
      
    public void setValue(Object val)  
    {  
        this.val=val;  
    }  
      
    public void showInfo()  
    {  
        System.out.println("Cache的ID是:   "+id+"   Cache的值是:   "+val);  
    }  
}  

问题1: 哪一步能够体现出缓存相对于直接读取库的优点?

实际上是getCache()方法,是从HashMap中读取,也就是从JVM中读取,处于内存中。

 

问题2: 缓存若未能命中,岂不是返回空的Cache了?

是的,所以我们需要对getCache进行改进,未能命中说明缓存里面没有,需要从数据库里面取数。

/** 
 *  项目名称: 
 *  文件说明:创建一个缓存管理器 <span style="font-family: Arial, Helvetica, sans-serif;">刘晨曦</span> 
 *  主要特点: 
 *  版本号:1.0 
 *  创建时间:2013-12-3 
 **/  
package NBOffer;  
  
import java.util.HashMap;  
  
public class CacheManager {  
  
    static HashMap<String,Cache> cacheMap=new HashMap<String,Cache>();  
      
    public static Cache getCache(String id)  
    {  
        if(cacheMap.get(id)==null)  
        {  
            Object val=getFromDB(id);  
            cacheMap.put(id, new Cache(id,val));  
        }  
        return cacheMap.get(id);  
    }  
      
    public static void putCache(Cache cache)  
    {  
        cacheMap.put(cache.id, cache);  
    }  
      
    public static Object getFromDB(String id)  
    {  
    System.out.println("缓慢地从内存中读取id="+id+"对应的数据。。。");     
    return new String("value"+id);  
    }  
      
    public static void main(String[] args) {  
        Cache cache1=new Cache("1","value1");  
        Cache cache2=new Cache("2","value2");  
        CacheManager.putCache(cache1);  
        CacheManager.putCache(cache2);  
        CacheManager.getCache("3").showInfo();  
    }  
}  
  
class Cache  
{  
    String id;//相当于主键  
    Object val;  
    public Cache(String id)  
    {  
        new Cache(id,null);  
    }  
      
    public Cache(String id,Object val)  
    {  
        this.id=id;  
        this.val=val;  
    }  
      
    public void setValue(Object val)  
    {  
        this.val=val;  
    }  
      
    public void showInfo()  
    {  
        System.out.println("Cache的ID是:   "+id+"   Cache的值是:   "+val);  
    }  
}

问题3:如果我想定时一段时间刷新缓存,比如,每隔15min刷新一次所有缓存,那我应该怎么办?

为了方便观察,我们设置为每间隔1s刷新一次。

/** 
 *  项目名称: 
 *  文件说明:创建一个缓存管理器 刘晨曦 
 *  主要特点: 
 *  版本号:1.0 
 *  创建时间:2013-12-3 
 **/  
package NBOffer;  
  
import java.util.HashMap;  
import java.util.Iterator;  
import java.util.Set;  
  
public class CacheManager {  
  
    static HashMap<String,Cache> cacheMap=new HashMap<String,Cache>();  
      
    public static Cache getCache(String id)  
    {  
        if(cacheMap.get(id)==null)  
        {  
            Object val=getFromDB(id);  
            cacheMap.put(id, new Cache(id,val));  
        }  
        return cacheMap.get(id);  
    }  
      
    public static void putCache(Cache cache)  
    {  
        cacheMap.put(cache.id, cache);  
    }  
      
    public static Object getFromDB(String id)  
    {  
    System.out.println("缓慢地从内存中读取id="+id+"对应的数据。。。");     
    return new String("value"+id);  
    }  
      
    public static void refreshCaches()  
    {  
        System.out.println("刷新缓存。。。");  
        Set<String> keySet=cacheMap.keySet();  
        Iterator it=keySet.iterator();  
        while(it.hasNext())  
        {  
            String id=(String) it.next();  
            Object val=getFromDB(id);  
            cacheMap.put(id, new Cache(id,val));  
        }  
    }  
      
    public static void main(String[] args) {  
        Cache cache1=new Cache("1","value1");  
        Cache cache2=new Cache("2","value2");  
        CacheManager.putCache(cache1);  
        CacheManager.putCache(cache2);  
        CacheManager.getCache("3").showInfo();  
          
        Thread refreshTD=new Thread()  
        {  
            public void run()  
            {  
                while(true)  
                {  
                    refreshCaches();  
                    try {  
                        Thread.sleep(1000);//每一秒刷新一次  
                    } catch (InterruptedException e) {  
                        e.printStackTrace();  
                    }  
                }  
            }  
        };  
        refreshTD.start();  
    }  
}  
  
class Cache  
{  
    String id;//相当于主键  
    Object val;  
      
    public Cache(String id)  
    {  
        new Cache(id,null);  
    }  
      
    public Cache(String id,Object val)  
    {  
        this.id=id;  
        this.val=val;  
    }  
      
    public void setValue(Object val)  
    {  
        this.val=val;  
    }  
      
    public void showInfo()  
    {  
        System.out.println("Cache的ID是:   "+id+"   Cache的值是:   "+val);  
    }  
} 

问题4:上面你说的缓存是存在问题的,因为每次你都是统一刷新,有的数据刚刚在14min59s的时候上传的,你就得让它在1s后刷新吗?另外,有的数据变化的很快,不能15min刷新的,而是需要没1min刷新一次,你这样做真的好吗?还有,有的数据在这15min中之内都不会用到,还有必要刷新吗?

上述三个问题的确需要改进,那么我可以针对不同的缓存,并且在自由需要(发送请求+缓存不存在/失效)的时候才进行刷新,再进行优化。代码如下:

/** 
 *  项目名称: 
 *  文件说明:创建一个缓存管理器 lcx 
 *  主要特点: 
 *  版本号:1.0 
 *  创建时间:2013-12-3 
 **/  
package NBOffer;  
  
import java.util.Date;  
import java.util.HashMap;  
  
public class CacheManager {  
  
    static HashMap<String,Cache> cacheMap=new HashMap<String,Cache>();  
  
    public static Cache getCache(String id)  
    {  
        //对于不存在或者失效的缓存统一处理  
        if(cacheMap.get(id)==null||!cacheMap.get(id).isValid())  
        {  
            Object val=getFromDB(id);  
            Cache cache=new Cache(id,val);  
            cache.latest=new Date();  
            cacheMap.put(id, cache);  
        }  
        return cacheMap.get(id);  
    }  
  
    public static void putCache(Cache cache)  
    {  
        cache.latest=new Date();  
        cacheMap.put(cache.id, cache);  
    }  
  
    public static Object getFromDB(String id)  
    {  
        System.out.println("缓慢地从内存中读取id="+id+"对应的数据。。。");     
        return new String("value"+id);  
    }  
  
    public static void main(String[] args) {  
        Cache cache1=new Cache("1","value1");  
        Cache cache2=new Cache("2","value2");  
        cache2.invalidTime=5000;//设置失效视角是5s  
        CacheManager.putCache(cache1);  
        CacheManager.putCache(cache2);  
        try {  
            Thread.sleep(6000);  
        } catch (InterruptedException e) {  
            e.printStackTrace();  
        }  
        CacheManager.getCache("1");  
        CacheManager.getCache("2");  
    }  
}  
class Cache  
{  
    String id;//相当于主键  
    Object val;  
    Date latest;//最近刷新时间  
    long invalidTime=10000;//默认失效时间10s  
  
    public Cache(String id)  
    {  
        new Cache(id,null);  
    }  
  
    public Cache(String id,Object val)  
    {  
        this.id=id;  
        this.val=val;  
    }  
  
    public void setValue(Object val)  
    {  
        this.val=val;  
    }  
  
    /** 
     * 判断现在缓存是否有效 
     * @return 
     */  
    public boolean isValid()  
    {  
        return (new Date()).getTime()-latest.getTime()<invalidTime;  
    }  
  
    public void showInfo()  
    {  
        System.out.println("Cache的ID是:   "+id+"   Cache的值是:   "+val);  
    }  
}  

问题5:既然都做到这个份上了,那么关于缓存的几个名词都得说说了。

 

缓存命中率:缓存命中率=从缓存中读数/(缓存读数+磁盘读数)。

缓存失效:缓存超过了失效时间。过期了(也可能有其他原因)。

缓存穿透:对于并不存在的数据,每次都会想DB发送请求,对DB的危害非常大。

缓存雪崩:对于某个失效的缓存在短时间内大量访问,可能造成DB宕机。

缓存算法:LRU、LFU、LIFO

posted @ 2017-10-19 10:57  烟雨观春柳  阅读(1100)  评论(0)    收藏  举报