在稍大型的项目里,常常为了解决查询数据的效率或者是为了减轻数据库的压力都会采用添加缓存来实现目的。那么在项目里我们该如何的添加缓存?这里我分享下我的使用缓存方案。
- 数据量不大的情况下:
在要缓存的数据量并不是太大的情况下,我们可以选择进行全量缓存。即全部缓存到内存里面,然后指定过一段时间对数据进行一次更新。每次调用方法获取数据时都先判断是否有进行过缓存或者是缓存是否过期,若有缓存且没过期则可以获取缓存里面的数据。
/**
* 获取缓存里面人员名称
*/
String getName(String id){
//调用判断方法
isCacheLoaded();
//从缓存(Map)里获取值,返回
}
void isCacheLoaded(){
//判断缓存(Map)是否为空,
//为空 加载 load()
//不为空判断上次加载缓存的时间到现在是否超过规定的时间
//超过 load()
}
private static final Object LOCK = new Object();
void load(){
//在这里若是有较高并发的要求则可以添加个锁用synchronize就可
loaded = false;
synchronized (LOADING_LOCK) {
if(!loaded){
//查询数据库获取数据
//放到缓存(Map)里面
loaded = true;
}
}
}
2 . 数据量比较大的情况下
在这种情况下,通常会使用外部缓存或者是内部缓存+外部缓存相结合使用。两个相结合的情况一般是这样:内部缓存自然是小而且过期时间快,外部缓存过期时间长。那么在不能进行全量缓存的情况下,该怎么做?
这时候一般是这样做:在最开始,内部缓存和外部缓存都是没有预先存值的。一个请求过来,查询某个数据。
查询内部缓存
—>有数据,返回。
—>没有数据,查询外部缓存
—>外部缓存有数据,得到,存入内部缓存,返回数据。
—>外部没有数据,查询数据库,
—>数据库里面没有数据,返回null
—>数据库有数据,存入外部缓存,存入内部缓存,返回值
就是这样的一个流程。
private static Map<Object,Object> lockMap = new ConcurrentHashMap<Object, Object>();
/**
* 获取缓存里面人员名称
*/
String getName(String id){
//调用获取缓存方法
String result = getCacheData(id);
if(result==null||result.length()==0){
//为了避免在高并发的情况下,查询同个id,
//在查询数据库这一阶段的时间停留过久,导致多次穿透缓存,查询多次数据库
//所以在这里添加一个与id关联的锁来保证
Object lock = lockMap.get(id);
if(lock==null){
lockMap.put(id,lock = new Object());
}
synchronize(lock){
result = getCacheData(id);//这里是再次从缓存里获取数据,避免再次查询数据库
if(result==null||result.length()==0){
//查询数据库,获取数据
//存入内部缓存,存入外部缓存,这样当其他线程进来锁时,
//再次查询缓存就有数据了,就不用再次进来查数据了
lockMap.remove(userId);
}
}
}
}
void getCacheData(String id){
//先查询内部缓存,内部缓存没有
//再次查询外部缓存
//返回值
}
3.其他情况:
比如我这个项目是可以允许部分脏数据的,那么可以这样做,在查询内外缓存后,有数据的情况,但是该数据过期了。那么返回这个过期的数据,自己立马起个线程去更新缓存。这样下次肯定就是新的数据。这里也是要添加个判断是否已经起了个线程去数据库获取新数据,不然可能会一下子来好多个线程做同一件事情,浪费资源。