为什么使用缓存?
- 降低数据库的访问压力。
- 提高查询效率。
- 改善用户体验。
你都了解哪些缓存?
- 数据库内置缓存(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);
}
}