当缓存数据不需分布式共享, 且数据规模不大时, 我们就可以考虑使用应用的本地缓存.
本地缓存的设计重点:
1, 过期删除策略
2, 缓存淘汰策略
过期删除策略
对于设置了过期时间的缓存,删除策略一般分为: 定期删除、惰性删除.
定期删除:单独的线程对数据进行过期检查
惰性删除:当数据被查询时再判断是否过期
缓存淘汰策略
当缓存空间不足时, 需要开启淘汰机制, 常见的淘汰策略有: FIFO、LRU、LFU
FIFO(First In First out): 先进先出
当资源不足时, 干掉那些先出生的老人, 简单粗暴, 但是可能造成经常使用的缓存被无辜淘汰.
LRU(Least recently used): 最近最少使用
LRU机制要求put缓存时,将数据放在链表的头部, get缓存时将数据移到链表的头部, 保证了最近使用的数据都在链表的头部区域, 最近最少使用的数据都在链表的尾部区域, 这样需要淘汰缓存时, 直接移除链表的尾部数据即可.
LFU(Least frequently used): 最少频率使用
LFU和LRU虽然都是基于“最少”使用,但“率”似乎更加准确, 就像现实中,我们经常用“增长率”来衡量增长指标一样.
上面3种淘汰策略, 最常使用的是LRU, 可以满足绝大多数场景, 实现起来也比较简单. 我们熟知的谷歌Guava Cache就采用了LRU, 但总有人精益求精, 提出一些新的策略, 比如Caffeine的W-TinyLFU. //Spring5已经弃用Guava Cache而选择了Caffeine.
如果一个Java小应用, 不需要考虑“缓存淘汰”,只需要考虑“缓存过期”, 那我们就可以自定义一个简单的非工业级的本地缓存, 如下:
import java.util.Date;
import java.util.concurrent.ConcurrentHashMap;
/**
* 简单本地缓存,适用于小规模的静态数据。
* 失效策略:"惰性删除",即每次查询时进行失效判断
* 淘汰策略:无
* @Author:tt
* @Description:
* @CreateTime:2018/11/11
*/
public class SimpleLocalCache {
//ConcurrentHashMap并发读写
private static ConcurrentHashMap<String, CacheData> cacheArea
= new ConcurrentHashMap<>();
//取缓存
public static Object get(String key) {
CacheData cacheData = cacheArea.get(key);
if (cacheData == null) {
return null;
}
if (isDead(cacheData)) {
//惰性删除
remove(key);
return null;
}
return cacheData.getValue();
}
//放缓存
public static boolean put(String key, Object value, long maxAge) {
CacheData data = new CacheData();
data.setCreateTime(new Date().getTime());
data.setMaxAge(maxAge);
data.setKey(key);
data.setValue(value);
cacheArea.put(key, data);
return false;
}
//是否过期
private static boolean isDead(CacheData data) {
if (data == null) {
return true;
}
if (data.getMaxAge() == 0) {
return false;
}
long createTime = data.getCreateTime();
long deadTime = createTime + data.getMaxAge();
long nowTime = new Date().getTime();
if (nowTime > deadTime) {
return true;
}
return false;
}
//移除
public static boolean remove(String key) {
cacheArea.remove(key);
return true;
}
//缓存数据封装
static class CacheData {
//创建时间:单位毫秒
private long createTime;
//缓存时常:单位毫秒, 0-标示永不失效
private long maxAge;
//数据key
private String key;
//数据value
private Object value;
//get、set方法略...
}
//测试
public static void main(String[] args) throws InterruptedException {
SimpleLocalCache.put("key1", "v1", 1000);
SimpleLocalCache.put("key2", "v2", 5000);
SimpleLocalCache.put("key3", "v3", 0);
Thread.sleep(3000);
System.out.println(SimpleLocalCache.get("key1"));//null
System.out.println(SimpleLocalCache.get("key2"));//v2
System.out.println(SimpleLocalCache.get("key3"));//v3
}
}