ehcache如何对复杂不定条件的java查询方法进行缓存?
怎么对这种复杂的查询进行缓存?!查询条件是不固定的,条件是由客户端传过来的参数确定的!
目前项目已经开发了4个月了,若使用注解的话整个项目的service层都得重写!
现行的方案采用的xml配置,在mapper.xml中加入<cache type="org.mybatis.caches.ehcache.EhcacheCache"/>,不需要缓存的地方加入useCache=“false”来解决的。但是这样会导致数据的操作非常密集。会报
CacheDecoratorFactory not configured for defaultCache. Skipping for 'com.dgdr.dao.ProductMapper'.
然后输出如下的日志很多次,即使服务器才启动都是如此:
2015-10-27 15:40:48.969 [com%002edgdr%002edao%002e%0050roduct%004dapper.data] DEBUG net.sf.ehcache.store.disk.Segment - fault removed 0 from heap
2015-10-27 15:40:48.970 [com%002edgdr%002edao%002e%0050roduct%004dapper.data] DEBUG net.sf.ehcache.store.disk.Segment - fault added 0 on disk
ehcache的XML配置:
<cache name="SimplePageCachingFilter" maxElementsInMemory="10000" eternal="false" timeToIdleSeconds="120" timeToLiveSeconds="120" overflowToDisk="true" maxElementsOnDisk="10000000" diskPersistent="false" diskExpiryThreadIntervalSeconds="120" memoryStoreEvictionPolicy="LRU"/>
望大牛们提点一下,这个问题如何解决。
cache的key设计有两种类型:
1.分页缓存
页缓存可以按查询条件来缓存,比如:
list_model_query1_query2_page_size
比如按用户名查询用户列表的key设计为:list_user_张三_1_20,业务比较复杂的话可以根据查询的方法自行做适配。
2.实体缓存
model_id
比如按id获取用户的key设计为:user_1
另外比如按用户名查询唯一的用户可以从db中得到id,之后再按id查询该用户对象。
这样通过页缓存可以得到所有的id集合,再通过id就可以查询得到对应的实体对象列表,你的这个业务其实在复杂抽象简化的话也是上面这两种数据模型,你可以根据你的需求去实现下。
1.技术背景:
<a href="https://www.baidu.com/s?wd=%E7%B3%BB%E7%BB%9F%E7%BC%93%E5%AD%98&tn=44039180_cpr&fenlei=mv6quAkxTZn0IZRqIHckPjm4nH00T1Y4Phm1rAw-ny7Wmhm1uhPh0ZwV5Hcvrjm3rH6sPfKWUMw85HfYnjn4nH6sgvPsT6K1TL0qnfK1TL0z5HD0IgF_5y9YIZ0lQzqlpA-bmyt8mh7GuZR8mvqVQL7dugPYpyq8Q1csnjfLPWmzP1nLrjb1rHTzPf" target="_blank" class="baidu-highlight">系统缓存</a>是位于应用程序与物理数据源之间,用于临时存放复制数据的内存区域,目的是为减少应用程序对物理数据源访问的次数,从而提高应用程序的运行性能。缓存设想内存是有限的,缓存的时效性也是有限的,所以可以设定内存数量的大小可以执行失效算法,可以在内存满了的情况下,按照最少访问等算法将缓存直接移除或切换到硬盘上。
Ehcache从Hibernate发展而来,逐渐涵盖了Cache界的全部功能,是目前发展势头最好的一个项目,具有快速、简单、低消耗、扩展性强、支持对象或序列化缓存,支持缓存或元素的失效,提供LRU、LFU和FIFO缓存策略,支持内存缓存和硬盘缓存和分布式缓存机制等特点。其中Cache的存储方式为内存或磁盘(ps:无须担心容量问题)
2.EhCahe的类层次介绍:
主要分为三层,最上层是CacheManager,它是操作Ehcache的入口。可以通过CacheManager.getInstance()获得一个单子的CacheManager,或者通过CacheManager的<a href="https://www.baidu.com/s?wd=%E6%9E%84%E9%80%A0%E5%87%BD%E6%95%B0&tn=44039180_cpr&fenlei=mv6quAkxTZn0IZRqIHckPjm4nH00T1Y4Phm1rAw-ny7Wmhm1uhPh0ZwV5Hcvrjm3rH6sPfKWUMw85HfYnjn4nH6sgvPsT6K1TL0qnfK1TL0z5HD0IgF_5y9YIZ0lQzqlpA-bmyt8mh7GuZR8mvqVQL7dugPYpyq8Q1csnjfLPWmzP1nLrjb1rHTzPf" target="_blank" class="baidu-highlight">构造函数</a>创建一个新的CacheManager。每个CacheManger都管理多个Cache。每个Cache都以一种类Hash的方式,关联多个Element。Element就是我们用于存放缓存内容的地方。
3.环境搭建:
很简单只需要将ehcache-2.1.0-distribution.tar.gz和ehcache-web-2.0.2-distribution.tar.gz挤压的jar包放入<a href="https://www.baidu.com/s?wd=WEB-INF&tn=44039180_cpr&fenlei=mv6quAkxTZn0IZRqIHckPjm4nH00T1Y4Phm1rAw-ny7Wmhm1uhPh0ZwV5Hcvrjm3rH6sPfKWUMw85HfYnjn4nH6sgvPsT6K1TL0qnfK1TL0z5HD0IgF_5y9YIZ0lQzqlpA-bmyt8mh7GuZR8mvqVQL7dugPYpyq8Q1csnjfLPWmzP1nLrjb1rHTzPf" target="_blank" class="baidu-highlight">WEB-INF</a>/lib下。
再创建一个重要的配置文件ehcache.xml,可以从ehcache组件包中拷贝一个,也可以自己建立一个,需要放到classpath下,一般放于/<a href="https://www.baidu.com/s?wd=WEB-INF&tn=44039180_cpr&fenlei=mv6quAkxTZn0IZRqIHckPjm4nH00T1Y4Phm1rAw-ny7Wmhm1uhPh0ZwV5Hcvrjm3rH6sPfKWUMw85HfYnjn4nH6sgvPsT6K1TL0qnfK1TL0z5HD0IgF_5y9YIZ0lQzqlpA-bmyt8mh7GuZR8mvqVQL7dugPYpyq8Q1csnjfLPWmzP1nLrjb1rHTzPf" target="_blank" class="baidu-highlight">WEB-INF</a>/classed/ehcache.xml;具体的配置文件可以网上搜一下
4.实际运用
一个网站的首页估计是被<a href="https://www.baidu.com/s?wd=%E8%AE%BF%E9%97%AE%E6%AC%A1%E6%95%B0&tn=44039180_cpr&fenlei=mv6quAkxTZn0IZRqIHckPjm4nH00T1Y4Phm1rAw-ny7Wmhm1uhPh0ZwV5Hcvrjm3rH6sPfKWUMw85HfYnjn4nH6sgvPsT6K1TL0qnfK1TL0z5HD0IgF_5y9YIZ0lQzqlpA-bmyt8mh7GuZR8mvqVQL7dugPYpyq8Q1csnjfLPWmzP1nLrjb1rHTzPf" target="_blank" class="baidu-highlight">访问次数</a>最多的,我们可以考虑给首页做一个页面缓存;
缓存策略:应该是某个固定时间之内不变的,比如说2分钟更新一次,以应用结构page-filter-action-service-dao-db为例。
位置:页面缓存做到尽量靠近客户的地方,就是在page和filter之间,这样的优点就是第一个用户请求后,页面被缓存,第二个用户在请求,走到filter这个请求就结束了,需要在走到action-service-dao-db,好处当然是服务器压力大大降低和客户端页面响应速度加快。
首页页面缓存存活时间定为2分钟,也就是参数timeToLiveSeconds(缓存的存活时间)应该设置为120,同时timeToIdleSeconds(多长时间不访问缓存,就清楚该缓存)最好也设为2分钟或者小于2分钟。
????
接着我们来看一下SimplePageCachingFilter 的配置,
<filter>
<filter-name>indexCacheFilterfilter-name>
<filter-class>
net.sf.ehcache.constructs.web.filter.SimplePageCachingFilter
<filter-class>
<filter>
<filter-mapping>
<filter-name>indexCacheFilterfilter-name>
<url-pattern>*index.actionurl-pattern>
<filter-mapping>
将上述代码加入到<a href="https://www.baidu.com/s?wd=web.xml&tn=44039180_cpr&fenlei=mv6quAkxTZn0IZRqIHckPjm4nH00T1Y4Phm1rAw-ny7Wmhm1uhPh0ZwV5Hcvrjm3rH6sPfKWUMw85HfYnjn4nH6sgvPsT6K1TL0qnfK1TL0z5HD0IgF_5y9YIZ0lQzqlpA-bmyt8mh7GuZR8mvqVQL7dugPYpyq8Q1csnjfLPWmzP1nLrjb1rHTzPf" target="_blank" class="baidu-highlight">web.xml</a>,那么当打开首页时,你会发现2分钟才会有一堆<a href="https://www.baidu.com/s?wd=sql%E8%AF%AD%E5%8F%A5&tn=44039180_cpr&fenlei=mv6quAkxTZn0IZRqIHckPjm4nH00T1Y4Phm1rAw-ny7Wmhm1uhPh0ZwV5Hcvrjm3rH6sPfKWUMw85HfYnjn4nH6sgvPsT6K1TL0qnfK1TL0z5HD0IgF_5y9YIZ0lQzqlpA-bmyt8mh7GuZR8mvqVQL7dugPYpyq8Q1csnjfLPWmzP1nLrjb1rHTzPf" target="_blank" class="baidu-highlight">sql语句</a>出现在控制台,也可以调整为5分钟,总之一切尽在掌控之中。
当然,如果你像缓存首页的部分内容时,你需要使用SimplePageFragmentCachingFilter这个filter,我看一下:
<filter>
<filter-name>indexCacheFilterfilter-name>
<filter-class>
net.sf.ehcache.constructs.web.filter.SimplePageFragmentCachingFilter
<filter-class>
filter>
<filter-mapping>
<filter-name>indexCacheFilterfilter-name>
<url-pattern>*/index_right.jsp<url-pattern>
<filter-mapping>
如此我们将jsp页面通过jsp:include到其他页面,这样就做到了页面局部缓存的效果,这一点貌似没有oscache的tag好用。
此外cachefilter中还有一个特性,就是gzip,也就是缓存中的元素是被压缩过的,如果客户端浏览器支持压缩的话,filter会直接返回压缩过的流,这样节省了带宽,把解压的工作交给了客户端浏览即可,当然如果客户端不支持gzip,那么filter会把缓存的元素拿出来解压后在返回给客户端浏览器(大多数爬虫是不支持gzip的,所以filter也会解压后在返回流)。
总之,Ehcache是一个非常轻量级的缓存实现,而且从1.2之后支持了集群,而且是hibernate默认的缓存provider,本文主要介绍Ehcahe对页面缓存的支持,但是它的功能远不止如此,要用好缓存,对<a href="https://www.baidu.com/s?wd=J2ee&tn=44039180_cpr&fenlei=mv6quAkxTZn0IZRqIHckPjm4nH00T1Y4Phm1rAw-ny7Wmhm1uhPh0ZwV5Hcvrjm3rH6sPfKWUMw85HfYnjn4nH6sgvPsT6K1TL0qnfK1TL0z5HD0IgF_5y9YIZ0lQzqlpA-bmyt8mh7GuZR8mvqVQL7dugPYpyq8Q1csnjfLPWmzP1nLrjb1rHTzPf" target="_blank" class="baidu-highlight">J2ee</a>中缓存的原理、适用范围、适用场景等等都需要比较深刻的理解,这样才能用好用对缓存。
为了大家通过实际例子加深了解与场景运用,在奉献一个实例:
*在Spring中运用EhCache
适用任意一个现有开源Cache Framework,要求可以Cache系统中service或者DAO层的get/find等方法返回结果,如果数据更新(适用了Create/update/delete),则刷新cache中相应的内容。
根据需求,计划适用Spring AOP+enCache来实现这个功能,采用ehCache原因之一就是Spring提供了enCache的支持,至于为何仅仅支持ehcache而不支持oscache和jbosscache就无从得知了。
AOP少不了拦截器,先创建一个实现了MethodInterceptor接口的拦截器,用来拦截Service/DAO的方法调用,拦截到方法后,搜索该方法的结果在cache中是否存在,如果存在,返回cache中结果,如果不存在返回数据库查询结果,并将结果返回到缓存。
public class MethodCacheInterceptor implements MethodInterceptor, InitializingBean
{
private static final Log logger = LogFactory.getLog(MethodCacheInterceptor.class);
private Cache cache;
public void setCache(Cache cache) {
this.cache = cache;
}
public MethodCacheInterceptor() {
super();
}
/**
* 拦截Service/DAO 的方法,并查找该结果是否存在,如果存在就返回cache 中的值,
* 否则,返回数据库查询结果,并将查询结果放入cache
*/
public Object invoke(MethodInvocation invocation) throws Throwable {
String targetName = invocation.getThis().getClass().getName();
String methodName = invocation.getMethod().getName();
Object[] arguments = invocation.getArguments();
Object result;
logger.debug("Find object from cache is " + cache.getName());
String cacheKey = getCacheKey(targetName, methodName, arguments);
Element element = cache.get(cacheKey);
Page 13 of 26
if (element == null) {
logger.debug("Hold up method , Get method result and create cache........!");
result = invocation.proceed();
element = new Element(cacheKey, (Serializable) result);
cache.put(element);
}
return element.getValue();
}
/**
* 获得cache key 的方法,cache key 是Cache 中一个Element 的唯一标识
* cache key 包括包名+类名+方法名,如com.co.cache.service.UserServiceImpl.getAllUser
*/
private String getCacheKey(String targetName, String methodName, Object[] arguments) {
StringBuffer sb = new StringBuffer();
sb.append(targetName).append(".").append(methodName);
if ((arguments != null) && (arguments.length != 0)) {
for (int i = 0; i < arguments.length; i++) {
sb.append(".").append(arguments[i]);
}
}
return sb.toString();
}
/**
* implement InitializingBean,检查cache 是否为空
*/
public void afterPropertiesSet() throws Exception {
Assert.notNull(cache, "Need a cache. Please use setCache(Cache) create it.");
}
}
上面的代码可以看到,在方法invoke中,完成了搜索cache/新建cache的功能
随后,再建立一个拦截器MethodCacheAfterAdvice,作用是在用户进行create/update/delete操作时来刷新、remove相关cache内容,这个拦截器需要实现AfterRetruningAdvice接口,将会在所拦截的方法执行后执行在afterReturning(object arg0,Method arg1,Object[] arg2,object arg3)方法中所预定的操作
public class MethodCacheAfterAdvice implements AfterReturningAdvice, InitializingBean
{
private static final Log logger = LogFactory.getLog(MethodCacheAfterAdvice.class);
private Cache cache;
Page 15 of 26
public void setCache(Cache cache) {
this.cache = cache;
}
public MethodCacheAfterAdvice() {
super();
}
public void afterReturning(Object arg0, Method arg1, Object[] arg2, Object arg3) throws
Throwable {
String className = arg3.getClass().getName();
List list = cache.getKeys();
for(int i = 0;i<list.size();i++){
String cacheKey = String.valueOf(list.get(i));
if(cacheKey.startsWith(className)){
cache.remove(cacheKey);
logger.debug("remove cache " + cacheKey);
}
}
}
public void afterPropertiesSet() throws Exception {
Assert.notNull(cache, "Need a cache. Please use setCache(Cache) create it.");
}
}
该方法获取目标class的全名,如:com.co.cache.test.TestServiceImpl,然后循环cache的key list,刷新/remove cache中所有和该class相关的element。
接着就是配置encache的属性,如最大缓存数量、cache刷新的时间等等。
<ehcache>
<diskStore path="c:\\myapp\\cache"/>
<defaultCache
maxElementsInMemory="1000"
eternal="false"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
overflowToDisk="true"
/>
<cache name="DEFAULT_CACHE"
maxElementsInMemory="10000"
eternal="false"
timeToIdleSeconds="300000"
timeToLiveSeconds="600000"
overflowToDisk="true"
/>
</ehcache>
这里需要注意的是defaultCache定义了一个默认的cache,这个Cache不能删除,否则会抛出No default cache is configured异常。另外由于使用拦截器来刷新Cache内容,因此在定义cache生命周期时可以定义较大的数值,timeToIdleSeconds="30000000",timeToLiveSeconds="6000000",好像还不够大?
然后再将Cache和两个拦截器配置到Spring的配置文件cache.xml中即可,需要创建两个“切入点”,分别用于拦截不同方法名的方法。在配置application.xml并且导入cache.xml。这样一个简单的Spring+Encache框架就搭建完成。
- 等 最代码怎么获取牛币啊?
- 完 谁来告诉我最代码上线的时间,答对者给5牛币,先来先得
- 等 牛友们,大家好,你们做程序员多久了?现在还好吗?
- 完 在微信打开的页面里进行app下载
- 等 最代码2014年欢乐聚声会
- 完 mysql如何查询表数据并且对3个字段降序的SQL?
- 完 最代码牛币机制改革
- 完 成功的在bae上使用了自定义运行环境 jetty+nginx的组合,大家对jetty+nginx优化有哪些心得?
- 完 进来分享一下各位牛牛是如何加入最代码大家庭的?
- 等 为什么java BufferedImage类处理大图直接抛出内存溢出的异常?
- 等 最代码是否开发手机app客户端?
- 完 java程序员学习哪些java的技术?java有哪些框架?都能做哪方面的开发?
- 等 php格式网页文件怎么运行?
- 等 Java volatile值获取的问题
- 等 前端vue,拦截了登录后台后,返回的token,requests拦截token,但是发送请求的时候,就出现跨越异常
- 等 大专本科计算机科班怎么找到Java工作?
- 等 eclipse怎么把三个java swing游戏项目合成一个项目?
- 完 伙伴们,大家都有什么好的解压方式么,分享一下~
- 完 三四线城市,6、7k,运维工作,索然无味,想去辞职上培训,各位牛牛有什么建议嘛
- 等 jsp页面输入中文变成问号
- 等 JPA在线上运行一段时间后报错Caused by: java.lang.IncompatibleClassChangeError: null
- 等 PHP 这个规则用preg_match_all怎么写
- 等 大佬们,有没有知道Alfresco如何配置LDAP登录呢?
- 等 php的install目录是框架带的吗?