为什么应用缓存?

  • 升高数据库的拜访压力。
  • 进步查问效率。
  • 改善用户体验。

你都理解哪些缓存?

  • 数据库内置缓存(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);  }    }

思考:对于SynchronizedCache 有什么劣势,劣势?

反对日志记录的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");  }    }

思考:你感觉LoggingCache记录日志的形式有什么不好的中央?(信息的完整性,同步问题)

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);  }    }

缓存零碎设计进阶

缓存利用需要降级

  • 缓存零碎既要保障线程平安又要保障性能。
  • 缓存日志的记录要写到文件,而且是异步写
  • 向缓存中写数据时要进步序列化性能。

缓存对象读写锁利用

package com.cy.java.cache;    import java.util.concurrent.locks.ReentrantReadWriteLock;    /**  * 构建线程平安对象,基于ReentrantReadWriteLock对象实现读写锁利用。  * @author qilei  */  public class ReentrantLockCache implements Cache {    private Cache cache;  /**  * 此对象提供了读锁,写锁利用形式.  * 1)写锁:排他锁  * 2)读锁:共享锁  * 阐明:读写不能同时执行。  */  private final ReentrantReadWriteLock readWriteLock =  new ReentrantReadWriteLock();  public ReentrantLockCache(Cache cache) {  this.cache=cache;  // TODO Auto-generated constructor stub }  @Override  public void putObject(Object key, Object value) {  readWriteLock.writeLock().lock();  try {  cache.putObject(key, value);  }finally {  readWriteLock.writeLock().unlock();  }  }    @Override  public Object getObject(Object key) {  readWriteLock.readLock().lock();  try {   Object object=cache.getObject(key);   return object;  }finally{  readWriteLock.readLock().unlock();  }  }    @Override  public Object removeObject(Object key) {  readWriteLock.writeLock().lock();  try {   Object object=cache.removeObject(key);   return object;  }finally{  readWriteLock.writeLock().unlock();  }  }    @Override  public void clear() {  readWriteLock.writeLock().lock();  try {   cache.clear();  }finally{  readWriteLock.writeLock().unlock();  }  }    @Override  public int size() {  readWriteLock.readLock().lock();  try {   int size=cache.size();   return size;  }finally{  readWriteLock.readLock().unlock();  }  }  }

异步日志Cache实现

第一步:增加依赖
` <dependency>   <groupId>ch.qos.logback</groupId>   <artifactId>logback-classic</artifactId>   <version>1.2.3</version>   </dependency> ` 
第二步:增加配置文件logback.xml (参考我的项目代码)
 <?xml version="1.0" encoding="UTF-8"?>  <configuration>    <logger name="com.cy" level="TRACE" />  <appender name="FILE"  class="ch.qos.logback.core.rolling.RollingFileAppender">  <rollingPolicy  class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">  <!--文件门路,定义了日志的切分形式----把每一天的日志归档到一个文件中,以避免日志填满整个磁盘空间 -->  <fileNamePattern>logs/context-log.%d{yyyy-MM-dd}.log  </fileNamePattern>  <!--只保留最近30天的日志 -->  <maxHistory>30</maxHistory>  </rollingPolicy>  <encoder charset="UTF-8">  <pattern>[%-5level] %date --%thread-- [%logger] %msg %n</pattern>  </encoder>  </appender>    <appender name="ASYNC_FILE"  class="ch.qos.logback.classic.AsyncAppender">  <discardingThreshold>0</discardingThreshold>  <queueSize>256</queueSize>  <appender-ref ref="FILE" />  </appender>    <root level="debug">  <appender-ref ref="ASYNC_FILE" />  </root>    </configuration>
第三步:构建AsyncLoggingCache
package com.cy.java.cache;    import org.slf4j.Logger;  import org.slf4j.LoggerFactory;    /**  * 通过此对象异步记录查问操作的命中率  * 1)抉择日志库  * 2)执行异步写操作。  */  public class AsyncLoggingCache implements Cache {   //日志门面利用  private static Logger log=LoggerFactory.getLogger(LoggingCache.class);  private Cache cache;  /**示意申请次数*/  private int requests;  /**命中次数(命中示意从缓存中取到数据了)*/  private int hits;  public AsyncLoggingCache(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++;   //记录日志耗时  log.info("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();  }  public static void main(String[] args) {  Cache cache=  new AsyncLoggingCache(new PerpetualCache());  cache.putObject("A", 100);  cache.putObject("B", 200);  cache.putObject("C", 300);  cache.putObject("D", 400);  //System.out.println(cache); cache.getObject("E");  cache.getObject("A");  cache.getObject("B");  }    }

Kryo构建序列化Cache

第一步:增加依赖
<dependency>   <groupId>com.esotericsoftware</groupId>   <artifactId>kryo</artifactId>   <version>5.0.0-RC5</version>  </dependency>
第二步:构建我的项目工具类
public class KryoUtils {    /**  * 多线程并发执行时,可能会呈现线程不平安,具体起因是什么?  * 1)多个线程的并发  * 2)多个线程有数据共享  * 3)多个线程在共享数据集上的操作不是原子操作  * * 剖析:当呈现了线程不平安,如何进行批改来保障线程平安  * 1)将多线程改为单线程。  * 2)勾销共享 (例如在以后利用中咱们一个线程一个Kryo对象)  * 3)加锁+CAS  * * ThreadLocal提供了这样的一种机制:  * 1)能够将对象绑定到以后线程(其实是将对象存储到以后线程的map中)  * 2)能够从以后线程获取绑定的对象(从以后线程的map中获取对象)  */  static private final ThreadLocal<Kryo> kryos = new ThreadLocal<Kryo>() {    protected Kryo initialValue() {    Kryo kryo = new Kryo();    // Configure the Kryo instance.   kryo.setRegistrationRequired(false);    //....   return kryo;    };   };   public static Object deserialize(byte[] array){  Kryo kryo=kryos.get();   Input input = new Input(new ByteArrayInputStream(array));   Object obj=kryo.readClassAndObject(input);  return obj;   }   public static byte[] serialize(Object object){   //从以后线程获取kryo对象,以后线程没有会调用ThreadLocal的initialValue办法创建对象并绑定线程  Kryo kryo=kryos.get();   ByteArrayOutputStream bos=new ByteArrayOutputStream();   Output output = new Output(bos);   kryo.writeClassAndObject(output, object);    output.close();   return bos.toByteArray();   }   }

> 构建高性能序列化Cache

public class KryoSerializedCache implements Cache {    private Cache cache;  public KryoSerializedCache(Cache cache) {  this.cache=cache;  }    @Override  public void putObject(Object key, Object value) {  //1.将对象序列化 byte[] array=KryoUtils.serialize(value);  //2.将序列化后的字节数组援用存储到cache cache.putObject(key,array);  }    @Override  public Object getObject(Object key) {  //1.基于key获取缓存中的字节数组援用 byte[] array=(byte[])cache.getObject(key);  //2.将字节数组反序列化为对象 return KryoUtils.deserialize(array);  }    @Override  public Object removeObject(Object key) {  return KryoUtils.deserialize((byte[])cache.removeObject(key));