Java中本地缓存实践
in Redis with 0 comment

Java中本地缓存实践

in Redis with 0 comment

简介

为什么要Cache

你都了解哪些Cache?

对本地缓存你会如何设计

本地缓存的设计

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缓存设计上做一个拔高.