简介
为什么要Cache
- 降低数据库的访问压力.
- 提高查询的性能,改善用户体验.
你都了解哪些Cache?
- 浏览器缓存
- 数据持久层的Cache(MyBatis中的Cache设计)
- 逻辑处理层的Cache(Spring中的Cache)
- CPU的高速缓存
对本地缓存你会如何设计
- 数据的存储结构(散列存储)
- 数据的淘汰算法(FIFO,LRU)
- 任务调度策略(定时刷新缓存)
- 缓存日志的记录(命中率)
- GC时会缓存数据的影响
本地缓存的设计
Cache接口设计
我们在设计Cache时,一般会先设计一个接口,定义一套规范,例如:
package com.jt.cache;
/**
* Cache 接口规范设计
*/
public interface Cache {
/**
* 存储数据
* @param key
* @param value
*/
void putObject(Object key,Object value);
/**
* 基于key获取数据
* @param key
* @return
*/
Object getObject(Object key);
/**
* 移除指定key的数据
* @param key
* @return
*/
Object removeObject(Object key);
/**
* 清空缓存
*/
void clear();
/**
* 获取缓存中数据的个数
* @return
*/
int size();
//...
}
Cache默认存储设计
设计一个Cache的默认存储对象,主要用于存储数据,例如:
package com.jt.cache;
import java.util.HashMap;
import java.util.Map;
public class DefaultCache implements Cache{
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 "DefaultCache{" +
"cache=" + cache.toString() +
'}';
}
public static void main(String[] args) {
Cache cache=new DefaultCache();
cache.putObject("A",100);
cache.putObject("B",200);
cache.putObject("C",300);
System.out.println(cache);
}
}
SynchronizedCache 设计及实现
线程安全的Cache设计及实现,例如:
package com.jt.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) {
return cache.getObject(key);
}
@Override
public synchronized 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() {
return "SynchronizedCache{" +
"cache=" + cache +
'}';
}
public static void main(String[] args) {
Cache cache = new SynchronizedCache(new DefaultCache());
cache.putObject("A", 100);
cache.putObject("B", 200);
System.out.println(cache);
}
}
LoggingCache 设计及实现
实际工作中我们经常要分析和监控缓存的命中率,例如我们设计了缓存,但为什么还是查询了数据库,有多少请求数据来自缓存,多少请求查询了数据库等,此时需要一个基于日志进行分析的一个过程,因此LoggingCache对象诞生,例如:
package com.jt.cache;
/**
* 通过此对象记录Cache命中率
*/
public class LoggingCache implements Cache{
/**对这个cache进行命中率的记录*/
private Cache cache;
/**请求次数*/
private int requests;
/**命中的次数*/
private int hints;
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) {
//1.记录请求次数
requests++;
//2.从cache获取数据
Object object = cache.getObject(key);
if(object!=null){
//计算命中率
hints++;
System.out.println("hits/requests is "+hints*1.0/requests);
}
return object;
}
@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) {
LoggingCache loggingCache =
new LoggingCache(new DefaultCache());
loggingCache.putObject("A",100);
loggingCache.putObject("B",200);
loggingCache.putObject("C",300);
loggingCache.getObject("D");
loggingCache.getObject("A");
loggingCache.getObject("A");
}
}
FifoCache设计及实现
缓存可以提供的内存空间是有限的,在缓存满的时候,我们要提供一些数据淘汰策略,接下来我们基于FIFO算法,对缓存进行设计,例如:
package com.jt.cache;
import java.util.Deque;
import java.util.LinkedList;
/**
* 有界缓存淘汰策略:FIFO(先进先出)算法
*/
public class FifoCache implements Cache{
/**存储数据的Cache*/
private Cache cache;
/**Cache的最大容量*/
private int maxCap;
/**双端队列(两头都可以操作),基于此队列记录key的顺序*/
private Deque<Object> deque;
public FifoCache(Cache cache, int maxCap) {
this.cache = cache;
this.maxCap = maxCap;
this.deque=new LinkedList<>();
}
@Override
public void putObject(Object key, Object value) {
//1.记录key的顺序
deque.addLast(key);
//2.判定cache是否已满,满了则移除元素
//if(cache.size()==maxCap){} 方式1
if(deque.size()>maxCap){//方式2
//获取最先放入的元素key
Object eldestKey=deque.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 value=cache.removeObject(key);
deque.remove(key);
return value;
}
@Override
public void clear() {
cache.clear();
deque.clear();
}
@Override
public int size() {
return cache.size();
}
@Override
public String toString() {
return "FifoCache{" +
"cache=" + cache +
'}';
}
public static void main(String[] args) {
FifoCache cache = new FifoCache(new DefaultCache(), 3);
cache.putObject("A",100);
cache.putObject("B",200);
cache.putObject("C",300);
cache.putObject("D",400);
cache.putObject("E",500);
System.out.println(cache);
}
}
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);
}
}
总结(Summary)
本章节为基础拓展章节,重点是通过本地缓存的设计,加强Java中一些基础点的理解,同时在Java缓存设计上做一个拔高.
本文由 liyunfei 创作,采用 知识共享署名4.0
国际许可协议进行许可
本站文章除注明转载/出处外,均为本站原创或翻译,转载前请务必署名
最后编辑时间为: Sep 22,2022