项目问题,没法在服务器上安装Radis缓存服务,而且没法改动原有的框架,因为原有框架上有很多模块,而且是深度定制的,想着自己构建一个简单的缓存得了。
路就这么开始走了。
根据项目特点,涉及大量的查询,而且仓库集市层的数据变化不会很快,频率应该是几天才会变一次,做缓存再合适不过了。
缓存缓存,说白了,就是把数据存在内存里,需要的时候去拿,在本质就是个Map对象。
首先我们构建一个简单的缓存。
/**
* 缓存对象
* 用于存取需要缓存的数据
* @author jianyuqin
* 2018年6月27日 10:47:10
*/
public class Cache {
private String cacheId;
private Object cacheObj;
/**
* 缓存对象构造函数
* @param cacheId 缓存ID
* @param cacheObj 缓存对象
*/
public Cache(String cacheId, Object cacheObj) {
this.cacheId = cacheId;
this.cacheObj = cacheObj;
}
public String getCacheId() {
return cacheId;
}
public Object getCacheObj() {
return cacheObj;
}
@Override
public String toString() {
return "Cache{" +
"cacheId='" + cacheId + '\'' +
", cacheObj='" + cacheObj + '\'' +
'}';
}
}
/**
* 缓存对象管理器
* @author jianyuqin
* 2018年6月27日 10:47:10
*/
@Component
public class CacheManager {
//放置缓存的Map对象
private static Map<String,Cache> cacheMap= null;
/**
* 缓存管理器初始化
*/
public CacheManager() {
if (cacheMap == null){
cacheMap = new HashMap<String, Cache>();
}
}
public void put(String cacheId,Object cacheObj) {
Cache cache = new Cache(cacheId,cacheObj);
cacheMap.put(cache.getCacheId(), cache);
}
public Object get(String cacheId) {
if (cacheMap.containsKey(cacheId)){
return cacheMap.get(cacheId).getCacheObj();
}else{
return null;
}
}
}
/**
* 得到备选模块数据(缓存测试方法)
*
* @param tjnyq
* @param tjnyz
* @param swjgDm
* @param list
*/
public Map<String, Object> getDateSelective(Integer tjnyq, Integer tjnyz, String swjgDm, String list) {
Map<String, Object> params = paramsParse(tjnyq,tjnyz,swjgDm);
Map<String, Object> result = null;
//缓存测试
String cacheId =list+tjnyq.toString()+tjnyz.toString()+swjgDm;
result = (Map<String, Object>) cacheManager.get(cacheId);
if (result !=null){
//命中缓存
System.out.println("[cache] shoot cache!......");
}else{
//未命中缓存
result = new HashMap<String, Object>();
for (String element : list.split(",")) {
if (element.equals("FZZZ-1")) {
result.put("FZZZ-1",getFZZC_1(params));
}else if (element.equals("FZZZ-2")) {
result.put("FZZZ-2",getFZZC_2(params));
}
}
//将结果集放入缓存
cacheManager.put(cacheId,result);
}
return result;
}
从数据库所取的数据大概13kb左右,时间测试数据如下
result prepared used 310 millis
[cache] shoot cache!......
result prepared used 1 millis
[cache] shoot cache!......
result prepared used 1 millis
[cache] shoot cache!......
result prepared used 0 millis
[cache] shoot cache!......
result prepared used 0 millis
[cache] shoot cache!......
result prepared used 0 millis
[cache] shoot cache!......
result prepared used 0 millis
[cache] shoot cache!......
result prepared used 0 millis
可以发现使用缓存之后,数据准备的时间几乎为0 。
做到这里,需求就来了,缓存怎么修改呢,或者准确一点说,缓存和数据库怎么同步呢?
一般来说缓存同步方式包括:
- Cache Aside 更新模式——同时更新数据库和缓存
- Read/Write Through 更新模式——先更新缓存,缓存负责同步更新数据库
- Write Behind Caching 更新模式——先更新缓存,缓存定时异步更新数据库
但是我这并不需要考虑太多数据同步的问题,我的数据表变化周期是以天为单位的,考虑到项目是大规模的查询,而且不是固定数据的查询,会涵盖一些数据随机组合的打包问题,我决定以包为单位进行缓存的存取和同步。
综上,有两种方案,一种是在固定的时间清空缓存,另一种是给每个缓存设置一个失效时间。
采用第二种方案。
/**
* 缓存对象
* 用于存取需要缓存的数据
* @author jianyuqin
* 2018年6月27日 10:47:10
*/
public class Cache {
private String cacheId;
private Object cacheObj;
private Long cacheFailureTime;
/**
* 缓存对象构造函数
* @param cacheId 缓存ID
* @param cacheObj 缓存对象
*/
public Cache(String cacheId, Object cacheObj) {
this.cacheId = cacheId;
this.cacheObj = cacheObj;
this.flushCacheFailureTime();
}
public String getCacheId() {
return cacheId;
}
public Object getCacheObj() {
return cacheObj;
}
/**
* 返回缓存是否失效
* @return
*/
public Boolean getCacheStatus() {
if (System.currentTimeMillis() - cacheFailureTime < CacheConstance.cacheFailureDuring)
return true;
this.cacheObj = null;
return false;
}
/**
* 刷新缓存失效时间,即初始化
*/
private void flushCacheFailureTime() {
this.cacheFailureTime = System.currentTimeMillis() + new Random().nextInt(10000);
}
@Override
public String toString() {
return "Cache{" +
"cacheId='" + cacheId + '\'' +
", cacheObj='" + cacheObj + '\'' +
'}';
}
}
/**
* 缓存对象管理器
* @author jianyuqin
* 2018年6月27日 10:47:10
*/
@Component
public class CacheManager {
//放置缓存的Map对象
private static Map<String,Cache> cacheMap= null;
/**
* 缓存管理器初始化
*/
public CacheManager() {
if (cacheMap == null){
cacheMap = new HashMap<String, Cache>();
}
}
public void put(String cacheId,Object cacheObj) {
Cache cache = new Cache(cacheId,cacheObj);
cacheMap.put(cache.getCacheId(), cache);
}
public Object get(String cacheId) {
if (cacheMap.containsKey(cacheId)){
//验证缓存失效
Cache cache = cacheMap.get(cacheId);
if (cache.getCacheStatus())
return cache.getCacheObj();
System.out.println("[cache] shoot failure cache!......");
return null;
}else{
return null;
}
}
}
可以看到,我在设置了固定缓存失效时间的基础上附加了一个随机数,这是为了防止并发很高时可能会出在某一个时间同时生成了很多的缓存,并且过期时间在同一时刻,这个时候就可能引发——当过期时间到后,这些缓存同时失效,请求全部转发到DB,DB可能会压力过重。
测试数据如下:
result prepared used 236 millis
[cache] shoot cache!......
result prepared used 1 millis
[cache] shoot failure cache!......
result prepared used 27 millis
[cache] shoot cache!......
result prepared used 0 millis
其实设计缓存还有许多其他的问题,诸如缓存穿透、缓存雪崩等等,由于项目环境原因这边不需要考虑,这一章就到这,下面章节开始优化缓存算法。