本地缓存(Cache)系统简易设计

为什么使用缓存?

  • 降低数据库的访问压力。
  • 提高查询效率。
  • 改善用户体验。

你都了解哪些缓存?

  • 数据库内置缓存(DBA修改)。
  • 数据层缓存(由持久层框架决定,例如mybatis)
  • 业务层缓存(由业务层框架以及第三缓存产品决定:本地缓存+分布式缓存)
  • 浏览器缓存(Cache-Control)

设计缓存都应该考虑什么问题?

  • 存储结构:使用什么结构存储数据?(数组,链表,散列存储-哈希存储)
  • 淘汰算法:有限容量(LRU,FIFO,.....),不限容量(GC)
  • 并发安全:保证线程安全。
  • 任务调度:每隔多长时间清理一下缓存。
  • 日志记录:是否命中?(命中率)

缓存系统设计基础

缓存标准定义

package com.cy.java.cache;  
/\*\* Cache接口设计\*/  
public interface Cache {  
     public void putObject(Object key,Object value);  
     public Object getObject(Object key);  
     public Object removeObject(Object key);  
     public void clear();  
     public int size();  
}

简易Cache实现

场景应用:
  • 存储数据量比较小(因为没有考虑淘汰机制)
  • 没有线程共享(一个线程的内部缓存)
  • 缓存对象生命周期比较短
package com.cy.java.cache;  
import java.util.HashMap;  
import java.util.Map;  
/**负责真正存储数据的一个对象,将数据存储到一个map中*/  
public class PerpetualCache implements Cache {  
    /** 特点:线程不安全,key不允许重复,不能保证key的顺序  */  
    private Map<Object,Object> cache=new HashMap<>();  
    @Override  
    public void putObject(Object key, Object value) {  
        cache.put(key, value);  
    }  
    @Override  
    public Object getObject(Object key) {  
        return cache.get(key);  
    }  
    @Override  
    public Object removeObject(Object key) {  
        return cache.remove(key);  
    }  
    @Override  
    public void clear() {  
         cache.clear();  
    }  
    @Override  
    public int size() {  
        return cache.size();  
    }  
    @Override  
    public String toString() {  
        return cache.toString();  
    }  
    public static void main(String\[\] args) {  
         Cache cache=new PerpetualCache();  
         cache.putObject("A", 100);  
         cache.putObject("B", 200);  
         cache.putObject("C", 300);  
         System.out.println(cache);  
         cache.removeObject("D");  
         cache.clear();  
         System.out.println(cache.size());  
    }  
}

构建线程安全Cache对象

场景应用:并发环境
package com.cy.java.cache;  
    /**线程安全的cache对象*/  
    public class SynchronizedCache implements Cache{  
    private Cache cache;  
    public SynchronizedCache(Cache cache) {  
        this.cache=cache;  
    }  
    @Override  
    public synchronized void putObject(Object key, Object value) {  
        cache.putObject(key, value);  
    }  
    @Override  
    public synchronized Object getObject(Object key) {  
        // TODO Auto-generated method stub  
        return cache.getObject(key);  
    }  
    @Override  
    public synchronized Object removeObject(Object key) {  
        // TODO Auto-generated method stub  
        return cache.removeObject(key);  
    }  
    @Override  
    public synchronized void clear() {  
        cache.clear();  
    }  
    @Override  
    public synchronized int size() {  
        return cache.size();  
    }  
    @Override  
    public String toString() {  
        return cache.toString();  
    }  
    public static void main(String\[\] args) {  
        SynchronizedCache cache=new SynchronizedCache(new PerpetualCache());  
        cache.putObject("A", 100);  
        cache.putObject("B", 200);  
        cache.putObject("C", 300);  
        System.out.println(cache);  
    }  
  
}

 支持日志记录的Cache实现

package com.cy.java.cache;  
/** 用于记录命中率的日志cache*/  
public class LoggingCache implements Cache {  
    private Cache cache;  
    /**记录请求次数*/  
    private int requests;  
    /**记录命中次数*/  
    private int hits;  
    public LoggingCache(Cache cache) {  
        this.cache=cache;  
    }  
    @Override  
    public void putObject(Object key, Object value) {  
        cache.putObject(key, value);  
    }  
    @Override  
    public Object getObject(Object key) {  
        requests++;  
        Object obj=cache.getObject(key);  
        if(obj!=null)hits++;  
        System.out.println("Cache hit Ratio : "+hits\*1.0/requests);  
        return obj;  
    }  
    @Override  
    public Object removeObject(Object key) {  
        return cache.removeObject(key);  
    }  
    @Override  
    public void clear() {  
        cache.clear();  
    }  
    @Override  
    public int size() {  
        return cache.size();  
    }  
    @Override  
    public String toString() {  
        // TODO Auto-generated method stub  
        return cache.toString();  
    }      
    public static void main(String\[\] args) {  
        SynchronizedCache cache= new SynchronizedCache(  
        new LoggingCache(new PerpetualCache()));  
        cache.putObject("A", 100);  
        cache.putObject("B", 200);  
        cache.putObject("C", 300);  
        System.out.println(cache);  
        cache.getObject("D");  
        cache.getObject("A");  
    }  
  
}

LruCache实现

应用场景:基于LRU算法的的基本实现
package com.cy.java.cache;  
import java.util.LinkedHashMap;  
import java.util.Map;  
  
/** 缓存淘汰策略:LRU(最近最少使用算法)*/  
public class LruCache implements Cache {  
     private Cache cache;  
     /**通过此属性记录要移除的数据对象*/  
     private Object eldestKey;  
     /**通过此map记录key的访问顺序*/  
     private Map<Object,Object> keyMap;  
     @SuppressWarnings("serial")  
     public LruCache(Cache cache,int maxCap) {  
         this.cache=cache;  
        //LinkedHashMap可以记录key的添加顺序或者访问顺序  
         this.keyMap=new LinkedHashMap<Object,Object>(maxCap, 0.75f, true)

         {//accessOrder  
            //此方法每次执行keyMap的put操作时调用  
            @Override  
            protected boolean removeEldestEntry (java.util.Map.Entry<Object, Object> eldest){  
                boolean isFull=size()>maxCap;  
                if(isFull)eldestKey=eldest.getKey();  
                return isFull;  
            }  
         };  
    }  
    @Override  
    public void putObject(Object key, Object value) {  
        //存储数据对象  
        cache.putObject(key, value);  
        //记录key的访问顺序,假如已经满了,就要从cache中移除数据  
        keyMap.put(key, key);//此时会执行keyMap对象的removeEldestEntry  
        if(eldestKey!=null) {  
            cache.removeObject(eldestKey);  
            eldestKey=null;  
        }  
    }  
    @Override  
    public Object getObject(Object key) {  
    keyMap.get(key);//记录key的访问顺序  
        return cache.getObject(key);  
    }  
  
    @Override  
    public Object removeObject(Object key) {  
        return cache.removeObject(key);  
    }  
  
    @Override  
    public void clear() {  
        cache.clear();  
        keyMap.clear();  
    }  
    @Override  
    public int size() {  
        return cache.size();  
    }  
    @Override  
    public String toString() {  
        return cache.toString();  
    }  
  
    public static void main(String\[\] args) {  
        SynchronizedCache cache= new SynchronizedCache(new LoggingCache(new LruCache(new PerpetualCache(),3)));  
        cache.putObject("A", 100);  
        cache.putObject("B", 200);  
        cache.putObject("C", 300);  
        cache.getObject("A");  
        cache.getObject("C");  
        cache.putObject("D", 400);  
        cache.putObject("E", 500);  
        System.out.println(cache);  
  
    }  
}

设置Cache淘汰算法:FIFO算法

package com.cy.java.cache;  
import java.util.Deque;  
import java.util.LinkedList;  
/**  
* FifoCache :基于FIFO算法(对象满了要按先进先出算法移除对象)实现cache对象  
*/  
public class FifoCache implements Cache{  
    /**借助此对象存储数据*/  
    private Cache cache;  
    /**借助此队列记录key的顺序*/  
    private Deque<Object> keyOrders;  
    /**通过此变量记录cache可以存储的对象个数*/  
    private int maxCap;  
    public FifoCache(Cache cache,int maxCap) {  
        this.cache=cache;  
        keyOrders=new LinkedList<>();  
        this.maxCap=maxCap;  
    }  
    @Override  
    public void putObject(Object key, Object value) {  
        //1.记录key的顺序(起始就是存储key,添加在队列最后位置)  
        keyOrders.addLast(key);  
        //2.检测cache中数据是否已满,满了则移除。  
        if(keyOrders.size()>maxCap) {  
            Object eldestKey=keyOrders.removeFirst();  
            cache.removeObject(eldestKey);  
        }  
        //3.放新的对象  
        cache.putObject(key, value);  
    }  
    @Override  
    public Object getObject(Object key) {  
        return cache.getObject(key);  
    }  
  
    @Override  
    public Object removeObject(Object key) {  
        Object obj=cache.removeObject(key);  
        keyOrders.remove(key);  
        return obj;  
    }  
    @Override  
    public void clear() {  
        cache.clear();  
        keyOrders.clear();  
    }  
    @Override  
    public int size() {  
        return cache.size();  
    }  
    @Override  
    public String toString() {  
        // TODO Auto-generated method stub  
        return cache.toString();  
    }  
    public static void main(String\[\] args) {  
        Cache cache= new SynchronizedCache(  
            new LoggingCache(  
                 new FifoCache(  
                      new PerpetualCache(),3)));  
        cache.putObject("A",100);  
        cache.putObject("B",200);  
        cache.putObject("C",300);  
        cache.getObject("A");  
        cache.putObject("D",400);  
        cache.putObject("E",500);  
        System.out.println(cache);  
    }       
}

序列化Cache的实现

场景:存储到cache的是对象的字节
package com.cy.java.cache;  
import java.io.ByteArrayInputStream;  
import java.io.ByteArrayOutputStream;  
import java.io.ObjectInputStream;  
import java.io.ObjectOutputStream;  
  
public class SerializedCache implements Cache {  
  
    private Cache cache;  
    public SerializedCache(Cache cache) {  
        this.cache=cache;  
    }  
    /**序列化*/  
    private byte\[\] serialize(Object value) {  
        //1.构建流对象  
        ByteArrayOutputStream bos=null;  
        ObjectOutputStream oos=null;  
        try {  
            //1.2构建字节数组输出流,此流对象内置可扩容的数组。  
            bos=new ByteArrayOutputStream();  
            //1.3构建对象输出流  
            oos=new ObjectOutputStream(bos);  
            //2.对象序列化  
            oos.writeObject(value);

             //此时对象会以字节的方式写入到字节数组输出流  
            oos.flush();  
            return bos.toByteArray();  
        }catch (Exception e) {  
             throw new RuntimeException(e);  
        }finally {  
            //3.关闭流对象  
            if(bos!=null)

             try{bos.close();bos=null;}catch(Exception e) {}  
            if(oos!=null)

             try{oos.close();oos=null;}catch (Exception e2) {}  
        }  
    }  
    /**反序列化*/  
    public Object deserialize(byte\[\] value) {  
        //1.创建流对象  
        ByteArrayInputStream bis=null;  
        ObjectInputStream ois=null;  
        try {  
            //1.1构建字节数组输入流,此对象可以直接读取数组中的字节信息  
            bis=new ByteArrayInputStream(value);  
            //1.2构建对象输入流(对象反序列化)  
            ois=new ObjectInputStream(bis);  
            //2.反序列化对象  
            Object obj=ois.readObject();  
            return obj;  
            }catch(Exception e) {  
                throw new RuntimeException(e);  
         }finally {  
             //3.关闭流对象  
             if(bis!=null)

                 try{bis.close();bis=null;}catch(Exception e) {}  
             if(ois!=null)

                 try{ois.close();ois=null;}catch (Exception e2) {}  
         }  
    }  
    @Override  
    public void putObject(Object key, Object value) {  
        cache.putObject(key, serialize(value));  
    }  
    @Override  
    public Object getObject(Object key) {  
        return deserialize((byte\[\])cache.getObject(key));  
    }  
    @Override  
    public Object removeObject(Object key) {  
        return cache.removeObject(key);  
    }  
    @Override  
    public void clear() {  
        cache.clear();  
    }  
    @Override  
    public int size() {  
        return cache.size();  
    }  
    public static void main(String\[\] args) {  
        Cache cache=new SerializedCache(new PerpetualCache());  
        cache.putObject("A", 200);  
        cache.putObject("B", 300);  
        Object v1=cache.getObject("A");  
        Object v2=cache.getObject("A");  
        System.out.println(v1==v2);  
        System.out.println(v1);  
        System.out.println(v2);  
    }  
}

 

软件引用Cache实现

应用场景:内存不足时淘汰缓存中数据
package com.cy.java.cache;  
  
import java.lang.ref.ReferenceQueue;  
import java.lang.ref.SoftReference;  
/**软引用*/  
public class SoftCache implements Cache {  
    private Cache cache;  
    private ReferenceQueue<Object> garbageOfRequenceQueue= new ReferenceQueue<>();  
    public SoftCache(Cache cache) {  
        this.cache=cache;  
    }  
    @Override  
    public void putObject(Object key, Object value) {  
        //1.移除一些垃圾对象(Soft引用引用的已经被回收的对象)  
        removeGarbageObjects();  
        //2.将对象存储到cache(key不变,Value为为soft引用对象)  
        cache.putObject(key, 

         new SoftEntry(key, value, garbageOfRequenceQueue));  
    }  
  
    @Override  
    public Object getObject(Object key) {  
        //1.基于key获取软引用对象并判断  
        SoftEntry softEntry=(SoftEntry)cache.getObject(key);  
        if(softEntry==null)return null;  
        //2.基于软引用对象获取它引用的对象并判断  
            Object target = softEntry.get();  
        if(target==null)cache.removeObject(key);  
        return target;  
    }  
  
    @Override  
    public Object removeObject(Object key) {  
        //1.移除一些垃圾对象(Soft引用引用的已经被回收的对象)  
        removeGarbageObjects();  
        //2.从cache中移除对象  
        Object removedObj=cache.removeObject(key);  
        return removedObj;  
    }  
  
    @Override  
    public void clear() {  
        //1.移除一些垃圾对象(Soft引用引用的已经被回收的对象)  
        removeGarbageObjects();  
        //2.清空cache  
        cache.clear();  
    }  
    @Override  
    public int size() {  
        removeGarbageObjects();  
        return cache.size();  
    }  
    private void removeGarbageObjects() {  
        SoftEntry softEntry=null;  
        //1.从引用队列中获取已经被GC的一些对象的引用  
         while((softEntry=

             (SoftEntry)garbageOfRequenceQueue.poll())!=null){  
            //softEntry不为null表示softEntry引用的对象已经被移除  
            //2.从cache中将对象引用移除。  
            cache.removeObject(softEntry.key);  
        }  
    }  
    /\*\*定义软引用类型\*/  
    private static class SoftEntry extends SoftReference<Object\>{  
        private final Object key;  
        public SoftEntry(Object key,Object referent, ReferenceQueue<? super Object> rQueue) {  
            super(referent, rQueue);  
            this.key=key;  
        }  
    }  
    @Override  
    public String toString() {  
        // TODO Auto-generated method stub  
        return cache.toString();  
    }  
  
    public static void main(String\[\] args) {  
        Cache cache=new SoftCache(new PerpetualCache());  
        cache.putObject("A", new byte\[1024\*1024\]);  
        cache.putObject("B", new byte\[1024\*1024\]);  
        cache.putObject("C", new byte\[1024\*1024\]);  
        cache.putObject("D", new byte\[1024\*1024\]);  
        cache.putObject("E", new byte\[1024\*1024\]);  
        System.out.println(cache.size());  
        System.out.println(cache);  
    }  
  
}

 

弱Cache对象实现

应用场景:GC触发清除缓存对象
package com.cy.java.cache;  
  
import java.lang.ref.ReferenceQueue;  
import java.lang.ref.WeakReference;  
/\*\*弱引用\*/  
public class WeakCache implements Cache {  
    private Cache cache;  
    private ReferenceQueue<Object> garbageOfRequenceQueue=new ReferenceQueue<>();  
    public WeakCache(Cache cache) {  
        this.cache=cache;  
    }  
  
    @Override  
    public void putObject(Object key, Object value) {  
        //1.移除一些垃圾对象(Soft引用引用的已经被回收的对象)  
        removeGarbageObjects();  
        //2.将对象存储到cache(key不变,Value为为soft引用对象)  
        cache.putObject(key, 

         new WeakEntry(key, value, garbageOfRequenceQueue));  
    }  
  
    @Override  
    public Object getObject(Object key) {  
        //1.基于key获取软引用对象并判断  
        WeakEntry softEntry=(WeakEntry)cache.getObject(key);  
        if(softEntry==null)return null;  
        //2.基于软引用对象获取它引用的对象并判断  
        Object target = softEntry.get();  
        if(target==null)cache.removeObject(key);  
        return target;  
    }  
  
    @Override  
    public Object removeObject(Object key) {  
        //1.移除一些垃圾对象(Soft引用引用的已经被回收的对象)  
        removeGarbageObjects();  
        //2.从cache中移除对象  
        Object removedObj=cache.removeObject(key);  
        return removedObj;  
    }  
  
    @Override  
    public void clear() {  
        //1.移除一些垃圾对象(Soft引用引用的已经被回收的对象)  
        removeGarbageObjects();  
        //2.清空cache  
        cache.clear();  
    }  
  
    @Override  
    public int size() {  
        removeGarbageObjects();  
        return cache.size();  
    }  
    private void removeGarbageObjects() {  
        WeakEntry softEntry=null;  
        //1.从引用队列中获取已经被GC的一些对象的引用  
        while((softEntry=(WeakEntry)garbageOfRequenceQueue.poll())!=null) {  
            //softEntry不为null表示softEntry引用的对象已经被移除  
            //2.从cache中将对象引用移除。  
            cache.removeObject(softEntry.key);  
        }  
    }  
    /**定义软引用类型*/  
    private static class WeakEntry extends WeakReference<Object\>{  
        private final Object key;  
        public WeakEntry(Object key,Object referent, ReferenceQueue<? super Object> rQueue) {  
            super(referent, rQueue);  
            this.key=key;  
        }  
    }  
    @Override  
    public String toString() {  
        return cache.toString();  
    }  
    public static void main(String\[\] args) {  
        Cache cache=new WeakCache(new PerpetualCache());  
        cache.putObject("A", new byte\[1024\*1024\]);  
        cache.putObject("B", new byte\[1024\*1024\]);  
        cache.putObject("C", new byte\[1024\*1024\]);  
        cache.putObject("D", new byte\[1024\*1024\]);  
        cache.putObject("E", new byte\[1024\*1024\]);  
        cache.putObject("F", new byte\[1024\*1024\]);  
        cache.putObject("G", new byte\[1024\*1024\]);  
        System.out.println(cache.size());  
        System.out.println(cache);  
    }  
  
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值