详解Cache机制

引用


一个Web站点可能存在一个“重量级”的加载,它能够使得站点在访问的时候,拖慢整个服务器。当站点被大量用户同时访问的时候,访问速度缓慢是大部分网站共同存在的问题。为了解决这个问题,采用缓存。
缓存是一种能够存储我们通常需要使用的数据的技术,它能够把web 页面暂时存储在本地的硬盘上以供后续的检索。这种技术在多个用户同时访问一个站点,或者一个用户多次访问一个站点时,有效地提升了访问速度。为Web应用程序做的缓存可以发生在客户端(浏览器缓存),可以作用在客户端和服务端之间的一个服务器上(代理缓存/反向代理缓存),或者只作用在web服务器本身(页面缓存或数据缓存)。
1、  客户端缓存
在采用客户端缓存时,客户端浏览器通过在本地硬盘上存储缓存的数据作为一个零时文件,或者存储在浏览器的内部存储器上来执行缓存操作。它提供了一种快速访问相同数据的方式,因为它拒绝任何的网络加载以及服务端加载。该数据不能够被其他客户端共享,所以是客户端独有的。
优势
a、  因为数据存储在本地客户端,所以可以很容易地访问
b、  避免了网络传输
劣势
a、  缓存的数据独立于浏览器,所以是无法被共享的
2、 代理缓存
客户端缓存的主要的劣势是数据存储在客户端浏览器,属于客户端私有的。而代理缓存使用一种独有的服务器在服务端与客户端之间以一种共享的“位置”来缓存数据,所以所有的客户端都可以使用相同的共享数据。代理服务器(例如微软的代理服务器)来满足所有对web页面的请求,而无需将请求跨越网络传输到最终的web服务器,这能够使得快速访问成为现实。
优势
a、  数据被缓存在代理服务器可以很容易就被访问
b、  减少网络通信
劣势
a、  涉及到部署以及为了维护代理缓存服务器的基础设施的开销
3、反向代理缓存
有些代理缓存服务器可以被防止到web 服务器的前端来减少他们接受到的请求的数量。它允许代理服务器处理通常接受到的请求,而仅仅传递其他的“特殊”请求给服务器。这称之为反向代理。
优势
a、  被缓存在反向代理服务器上的数据可以很容易地获得
b、  减少请求的数目
劣势
a、  当该服务器被配置在web服务器的前端,它可能导致额外的网络通信
4、web服务器缓存
在web服务端做缓存,缓存的数据被存储在web服务器上。数据缓存以及页面缓存可以使用web服务器这种缓存方案。
优势
a、  提高站点的性能,减少了从数据库以及其他服务器检索数据的开销。
劣势
b、  增加了网络加载开销
缓存的优势
a、  减少服务端的负载
b、  减少了带宽的消耗
输出缓存位置
就像我刚才提到的,我们可以存储缓存数据在不同的地方——客户端,服务器,代理服务器。现在,我将怎么设置缓存数据的位置。如果我们存储缓存数据,它将从缓存中检索出页面从而节省了页面的生成时间。但是另一种方式是可以保存数据在客户端浏览器,它可以减少网络通信。而OutputCache执行能够使用所有的三种方式的缓存——服务器,客户端,代理(默认)。
接下来的飙歌展示了缓存位置的明细。它展示了缓存处在的位置,以及对Cache-Control和过时间头的影响。
例如,如果你为Location属性设置Client值,页面将不会被保存在服务端的缓存中,但是响应中将包含一个Cache-Control响应头(通过使用Cache-Control头,页面可以指明是否他们应该被缓存在一个代理上)设值为private,并且一个Expires头(Http 响应,指明该页面需要重新从服务器请求的日期和时间)值为一个时间段,它是由Duration属性指明的。
缓存依赖
使用缓存依赖,我们可以为某些数据或可能改变的实体设置依赖。所以我们能够通过设置缓存依赖来更新或者移除缓存。在asp.net中支持三种类型的依赖:
(1)      基于文件的依赖
(2)      基于键的依赖
(3)      基于时间的依赖
基于文件的依赖:基于文件的依赖,在当磁盘上的文件改变时,可以让一个通常的缓存项失效。
使用缓存依赖,当文件改变时,我们可以从缓存中强制失效某些缓存项。我们可以设置多个文件依赖。在这样的情况下,依赖应该被建立在一组文件或文件夹上。
使用:基于文件的依赖是非常有用的,当你需要更新更新的数据是基于某个文件的时候。例如,一个新闻站点总是从一个文件中去获取数据,但是,如果某些爆炸性的新闻出来,他们只需要更新下文件,然后缓存就该失效,并且在失效时间之内,我们通过OnRemoveCallBack回调可以重新加载缓存中的已被更新的数据。
基于键的缓存依赖:基于键的依赖,在当另一个缓存项改变时,让一个通常的缓存项失效。
使用:当我们有多个内部关联对象在缓存中时,这将会非常有用,我们需要更新或过期所有的这些对象。
基于时间的缓存依赖:基于时间的依赖会让一个缓存项在预定的时间失效。Cache的Cache.Insert()方法被用来创建一个基于时间的缓存依赖。可以对其设置两种类型的时间:
(1)      绝对时间
(2)      “滑动”时间(相对的)
绝对:给一个缓存项设置一个失效的绝对时间。它是一个全时间格式,包含(hh:mm:ss)。
滑动:对每个请求重置缓存项的失效时间。当缓存项需要为了来自多个客户端的请求都保持存活的时候,它是非常有用的。
对这些所有的依赖,asp.net允许下面的操作发生:
自动失效:那些在使用中并且没有任何依赖的缓存项都会自动失效。
支持回调:缓存对象能够被配置来调用一个被“赋予”的一段代码,当一个缓存项被从缓存中移除时,这些代码就会被调用。这给了你机会去更新缓存。我们可以使用OnRemoveCallback()。
缓存类型的建议
场景一:生成的页面通常都相同,但是有某些展示的表单通常会更新(使用页面片段缓存)
场景二:生成的页面总是在变化,但是有一些对象并不是经常变动(使用数据缓存)
场景三:生成的页面每隔几个小时变化一次,当信息通过一种自动处理过程从数据库中被加载时(使用页面输出缓存并且设置过期时间来匹配数据库的更新时间)。
缓存可分为二大类:
    一、通过文件缓存,顾名思义文件缓存是指把数据存储在磁盘上,不管你是以XML格式,序列化文件DAT格式还是其它文件格式; 
    二、内存缓存,也就是实现一个类中静态Map,对这个Map进行常规的增删查.
    HashMap:关键字Key是唯一不重复的,Map是一个有序的集合,所以查询起来特别快


引用

第一部分:Cache的原理
1.这个是java应用中间常用的缓存,最常使用的场合就是访问数据库的时候为了提高效率而使用的 cache。一般的用法就是把数据从数据库读到内存,然后之后的数据访问都从内存来读,从而减少对数据库的读取次数来提高效率。  在使用cache的时候最容易犯的错误就是cache涉及了业务逻辑。使用cache的原意是只是提高程序效率,而不应该干涉程序结果。
2常用的算法

    最近最少使用算法 Least Recently Used (LRU):

这个算法就是把最近一次使用时间离现在时间最远的数据删除掉。最直观的结构应该是List,采取的算法是:每次访问一个元素后把这个元素放在 List一端,这样一来最远使用的元素自然就被放到List的另一端。每次evict的时候就把那最远使用的元素remove掉。但是现实中常采用的数据 结构是HashMap + List。因为List太慢,List只能提供O(n)的算法,要使得它的add,remove和get的算法为O(1)就必须使用HashMap。最简 单的实现就是利用JDK自带的LinkedHashMap,你可以把它看作普通的HashMap之外,每个元素的key都用链表连接起来从而实现顺序结 构。LinkedHashMap默认的元素顺序是put的顺序,但是如果使用带参数的构造函数,那么LinkedHashMap会根据访问顺序来调整内部 顺序。 LinkedHashMap的get()方法除了返回元素之外还可以把被访问的元素放到链表的底端,这样一来每次顶端的元素就是remove的元素。


package com.cache.sym;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
public class CacheManager {
	/**
	 * @param args
	 * 本demo采用的是内存缓存,当需要变成文本缓存时,只需要改换存储方式
	 */
	private static HashMap cacheMap = new HashMap();   
    //单实例构造方法   
    private CacheManager() {   
        super();   
    }   
    //获取布尔值的缓存   
    public static boolean getSimpleFlag(String key){   
        try{   
            return (Boolean) cacheMap.get(key);   
        }catch(NullPointerException e){   
            return false;   
        }   
    }   
    public static long getServerStartdt(String key){   
        try {   
            return (Long)cacheMap.get(key);   
        } catch (Exception ex) {   
            return 0;   
        }   
    }   
    //设置布尔值的缓存   
    public synchronized static boolean setSimpleFlag(String key,boolean flag){   
        if (flag && getSimpleFlag(key)) {//假如为真不允许被覆盖   
            return false;   
        }else{   
            cacheMap.put(key, flag);   
            return true;   
        }   
    }   
    public synchronized static boolean setSimpleFlag(String key,long serverbegrundt){   
        if (cacheMap.get(key) == null) {   
            cacheMap.put(key,serverbegrundt);   
            return true;   
        }else{   
            return false;   
        }   
    }   
    //得到缓存。同步静态方法   
    private synchronized static Cache getCache(String key) {   
        return (Cache) cacheMap.get(key);   
    }   
    //判断是否存在一个缓存   
    private synchronized static boolean hasCache(String key) {   
        return cacheMap.containsKey(key);   
    }   
    //清除所有缓存   
    public synchronized static void clearAll() {   
        cacheMap.clear();   
    }   
    //清除某一类特定缓存,通过遍历HASHMAP下的所有对象,来判断它的KEY与传入的TYPE是否匹配   
    public synchronized static void clearAll(String type) {   
        Iterator i = cacheMap.entrySet().iterator();   
        String key;   
        ArrayList<String> arr = new ArrayList<String>();   
        try {   
            while (i.hasNext()) {   
                java.util.Map.Entry entry = (java.util.Map.Entry) i.next();   
                key = (String) entry.getKey();   
/*
 * 根据自己的需求进行清除
 */
                if (key.startsWith(type)) { //如果匹配则删除掉   
                    arr.add(key);   
                }   
            }   
            for (int k = 0; k < arr.size(); k++) {   
                clearOnly(arr.get(k));   
            }   
        } catch (Exception ex) {   
            ex.printStackTrace();   
        }   
    }   
    //清除指定的缓存   
    public synchronized static void clearOnly(String key) {   
        cacheMap.remove(key);   
    }   
    //载入缓存   
    public synchronized static void putCache(String key, Cache obj) {   
        cacheMap.put(key, obj);   
    }   
    //获取缓存信息   
    public static Cache getCacheInfo(String key) {   
        if (hasCache(key)) {   
            Cache cache = getCache(key);   
            if (cacheExpired(cache)) { //调用判断是否终止方法   
                cache.setExpired(true);   
            }   
            return cache;   
        }else  
            return null;   
    }   
    //载入缓存信息   ,若为文本缓存,则此处就是进行文本的操作 
    public static void putCacheInfo(String key, Cache obj, long dt,boolean expired) {   
        Cache cache = new Cache();   
        cache.setKey(key);   
        cache.setTimeOut(dt + System.currentTimeMillis()); //设置多久后更新缓存   
        cache.setValue(obj);   
        cache.setExpired(expired); //缓存默认载入时,终止状态为FALSE   
        cacheMap.put(key, cache);   
    }   
    //重写载入缓存信息方法   
    public static void putCacheInfo(String key,Cache obj,long dt){   
        Cache cache = new Cache();   
        cache.setKey(key);   
        cache.setTimeOut(dt+System.currentTimeMillis());   
        cache.setValue(obj);   
        cache.setExpired(false);   
        cacheMap.put(key,cache);   
    }   

  

    //判断缓存是否终止   
    public static boolean cacheExpired(Cache cache) {   
        if (null == cache) { //传入的缓存不存在   
            return false;   
        }   
        long nowDt = System.currentTimeMillis(); //系统当前的毫秒数   
        long cacheDt = cache.getTimeOut(); //缓存内的过期毫秒数   
        if (cacheDt <= 0||cacheDt>nowDt) { //过期时间小于等于零时,或者过期时间大于当前时间时,则为FALSE   
            return false;   
        } else { //大于过期时间 即过期   
            return true;   
        }   
    }   
    //获取缓存中的大小   
    public static int getCacheSize() {   
        return cacheMap.size();   
    }   
    //获取指定的类型的大小   
    public static int getCacheSize(String type) {   
        int k = 0;   
        Iterator<Map.Entry> i = cacheMap.entrySet().iterator();   
        String key;   
        try {   
            while (i.hasNext()) {   
              Map.Entry entry = i.next();   
                key = (String) entry.getKey();   
                if (key.indexOf(type) != -1) { //如果匹配则删除掉   
                    k++;   
                }   
            }   
        } catch (Exception ex) {   
            ex.printStackTrace();   
        }   
        return k;   
    }   
    //获取缓存对象中的所有键值名称   
    public static ArrayList<String> getCacheAllkey() {   
        ArrayList a = new ArrayList();   
        try {   
            Iterator i = cacheMap.entrySet().iterator();   
            while (i.hasNext()) {   
                java.util.Map.Entry entry = (java.util.Map.Entry) i.next();   
                a.add((String) entry.getKey());   
            }   
        } catch (Exception ex) {} finally {   
            return a;   
        }   
    }   
    //获取缓存对象中指定类型 的键值名称   
    public static ArrayList<String> getCacheListkey(String type) {   
        ArrayList a = new ArrayList();   
        String key;   
        try {   
            Iterator i = cacheMap.entrySet().iterator();   
            while (i.hasNext()) {   
                java.util.Map.Entry entry = (java.util.Map.Entry) i.next();   
                key = (String) entry.getKey();   
                if (key.indexOf(type) != -1) {   
                    a.add(key);   
                }   
            }   

        } catch (Exception ex) {} finally {   
            return a;   
        }   
    }   
    public static void main(String[] args) {   
        System.out.println(CacheManager.getSimpleFlag("alksd"));   
        CacheManager.putCache("abc", new Cache());   
        CacheManager.putCache("def", new Cache());   
        CacheManager.putCache("ccc", new Cache());   
        CacheManager.clearOnly("");   
        Cache c = new Cache();   
        for (int i = 0; i < 10; i++) {   
            CacheManager.putCache("" + i, c);   
       }   
        System.out.println(CacheManager.getCacheSize());
        CacheManager.putCache("aaaaaaaa", c); 
        CacheManager.putCache("f4taaaa", c); 
        CacheManager.putCache("abchcy", c);   
        CacheManager.putCache("cccccccc", c);   
        CacheManager.putCache("abcoqiwhcy", c);   
        CacheManager.putCache("fefegeg", c);   
        System.out.println("删除前的大小:"+CacheManager.getCacheSize());   
        CacheManager.getCacheAllkey();   
        System.out.println( CacheManager.getCacheAllkey());
        CacheManager.clearAll("aaaa");   
        System.out.println("删除后的大小:"+CacheManager.getCacheSize());   
        CacheManager.getCacheAllkey();   
        System.out.println( CacheManager.getCacheAllkey());
  /*
         * 打印:删除前的大小:19
[abc, abcoqiwhcy, aaaaaaaa, abchcy, 3, 2, 1, 0, def, 7, 6, fefegeg, cccccccc, 5, 4, f4taaaa, ccc, 9, 8]
删除后的大小:18
[abc, abcoqiwhcy, abchcy, 3, 2, 1, 0, def, 7, 6, fefegeg, cccccccc, 5, 4, f4taaaa, ccc, 9, 8]

         */
    }   

}   
package com.cache.sym;

public class Cache {   
    private String key;//缓存ID   
    private Object value;//缓存数据   
    private long timeOut;//更新时间   
    private boolean expired; //是否终止   
    public Cache() {   
            super();   
    }   
    public Cache(String key, Object value, long timeOut, boolean expired) {   
            this.key = key;   
            this.value = value;   
            this.timeOut = timeOut;   
            this.expired = expired;   
    }   
    public String getKey() {   
            return key;   
    }   
    public long getTimeOut() {   
            return timeOut;   

    }   
    public Object getValue() {   
            return value;   
    }   
    public void setKey(String string) {   
            key = string;   
    }   
    public void setTimeOut(long l) {   
            timeOut = l;   
    }   
    public void setValue(Object object) {   
            value = object;   
    }   
    public boolean isExpired() {   
            return expired;   
    }   
    public void setExpired(boolean b) {   
            expired = b;   
    }   
}   
  




    package global;  
      
    import java.lang.reflect.Field;  
    import java.util.*;  
      
    import org.apache.log4j.Level;  
      
    import com.bruce.util.Logger;  
      
    import vo.Experence;  
    import vo.Gender;  
    import vo.Interest;  
      
    public class Cache  
    {  
            /** 保存在 Servlet Context 中的缓存对象的 Key */  
            public static final String CACHE_KEY    = "__cache";  
            /** 全局唯一缓存对象 */  
            private static final Cache instance             = new Cache();  
             
            /** 性别列表 */  
            private List<Gender> genders                                    = new ArrayList<Gender>();  
            /** 性别 Map */  
            private Map<Integer, Gender> gendersMap                 = new HashMap<Integer, Gender>();  
            /** 兴趣列表 */  
            private List<Interest> interests                                = new ArrayList<Interest>();  
            /** 兴趣 Map */  
            private Map<Integer, Interest> interestsMap             = new HashMap<Integer, Interest>();  
            /** 工作年限列表 */  
            private List<Experence> experences                              = new ArrayList<Experence>();  
            /** 工作年限 Map */  
            private Map<Integer, Experence> experencesMap   = new HashMap<Integer, Experence>();  
             
            /** 私有构造函数 */  
            private Cache()  
            {  
                     
            }  
             
            /** 缓存对象获取方法 */  
            public static final Cache getInstance()  
            {  
                    return instance;  
            }  
      
            /** 加载基础数据缓存 */  
            synchronized void loadBasicData()  
            {  
                    genders.add(new Gender(1, "男"));  
                    genders.add(new Gender(2, "女"));  
                     
                    for(Gender o : genders)  
                            gendersMap.put(o.getId(), o);  
                             
                    interests.add(new Interest(1, "游泳"));  
                    interests.add(new Interest(2, "打球"));  
                    interests.add(new Interest(3, "下棋"));  
                    interests.add(new Interest(4, "打麻将"));  
                    interests.add(new Interest(5, "看书"));  
                     
                    for(Interest o : interests)  
                            interestsMap.put(o.getId(), o);  
                             
                    experences.add(new Experence(1, "3 年以下"));  
                    experences.add(new Experence(2, "3-5 年"));  
                    experences.add(new Experence(3, "5-10 年"));  
                    experences.add(new Experence(4, "10 年以上"));  
                     
                    for(Experence o : experences)  
                            experencesMap.put(o.getId(), o);  
            }  
             
            /** 卸载基础数据缓存 */  
            synchronized void unloadBasicData()  
            {  
                    Field[] fields = this.getClass().getDeclaredFields();  
      
                    for(Field f : fields)  
                    {  
                            Class<?> type = f.getType();  
                             
                            if(type.isAssignableFrom(List.class) || type.isAssignableFrom(Map.class))  
                            {  
                            try  
                            {  
                                    f.set(this, null);  
                            }  
                            catch(Exception e)  
                            {  
                                    Logger.exception(e, String.format("unload basic data '%s'", f), Level.ERROR, true);  
                            }  
                            }  
                    }  
            }  
             
            /** 通过 ID 查找 Gender 对象 */  
            public Gender getGenderById(Integer id)  
            {  
                    return gendersMap.get(id);  
            }  
      
            /** 通过 ID 查找 Interest 对象 */  
            public Interest getInterestById(Integer id)  
            {  
                    return interestsMap.get(id);  
            }  
      
            /** 通过 ID 查找 Experence 对象 */  
            public Experence getExperenceById(Integer id)  
            {  
                    return experencesMap.get(id);  
            }  
      
            public List<Gender> getGenders()  
            {  
                    return genders;  
            }  
      
            public Map<Integer, Gender> getGendersMap()  
            {  
                    return gendersMap;  
            }  
      
            public List<Interest> getInterests()  
            {  
                    return interests;  
            }  
      
            public Map<Integer, Interest> getInterestsMap()  
            {  
                    return interestsMap;  
            }  
      
            public List<Experence> getExperences()  
            {  
                    return experences;  
            }  
      
            public Map<Integer, Experence> getExperencesMap()  
            {  
                    return experencesMap;  
            }  
    }  

猜你喜欢

转载自vvsongsunny.iteye.com/blog/2035378